Changeset 8db20a9 in sasview for src


Ignore:
Timestamp:
Jan 21, 2019 8:15:40 AM (6 years ago)
Author:
Piotr Rozyczko <piotr.rozyczko@…>
Branches:
ESS_GUI, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_opencl, ESS_GUI_sync_sascalc
Children:
d541324e
Parents:
3ca645bb
git-author:
Piotr Rozyczko <piotr.rozyczko@…> (01/21/19 08:12:08)
git-committer:
Piotr Rozyczko <piotr.rozyczko@…> (01/21/19 08:15:40)
Message:

Updated cansas read (cherrypicked and fixed from master).
Fixes: hdf5 returns byte strings so these need to be recasted properly.
https://github.com/h5py/h5py/issues/379

Location:
src/sas
Files:
4 added
15 edited

Legend:

Unmodified
Added
Removed
  • src/sas/qtgui/Plotting/Plotter2D.py

    r21e71f1 r8db20a9  
    468468        # check scale 
    469469        if self.scale == 'log_{10}': 
    470             try: 
    471                 if  self.zmin is None  and len(output[output > 0]) > 0: 
    472                     zmin_temp = self.zmin 
    473                     output[output > 0] = numpy.log10(output[output > 0]) 
    474                 elif self.zmin <= 0: 
    475                     zmin_temp = self.zmin 
    476                     output[output > 0] = numpy.zeros(len(output)) 
    477                     output[output <= 0] = MIN_Z 
    478                 else: 
    479                     zmin_temp = self.zmin 
    480                     output[output > 0] = numpy.log10(output[output > 0]) 
    481             except: 
    482                 #Too many problems in 2D plot with scale 
    483                 pass 
     470            with numpy.errstate(all='ignore'): 
     471                output = numpy.log10(output) 
     472            index = numpy.isfinite(output) 
     473            if not index.all(): 
     474                cutoff = (numpy.quantile(output[index], 0.05) - numpy.log10(2) if index.any() else 0.) 
     475                output[output < cutoff] = cutoff 
     476                output[~index] = cutoff 
     477        vmin, vmax = None, None 
    484478 
    485479        self.cmap = cmap 
  • src/sas/qtgui/Utilities/GuiUtils.py

    r10786bc2 r8db20a9  
    4444 
    4545from sas.sascalc.dataloader.loader import Loader 
     46from sas.sascalc.file_converter.nxcansas_writer import NXcanSASWriter 
     47 
    4648from sas.qtgui.Utilities import CustomDir 
    4749 
     
    793795 
    794796    wildcard = "Text files (*.txt);;"\ 
    795                 "CanSAS 1D files(*.xml)" 
     797                "CanSAS 1D files(*.xml);;"\ 
     798                "NXcanSAS files (*.h5)" 
    796799    kwargs = { 
    797800        'caption'   : 'Save As', 
     
    812815    if os.path.splitext(filename)[1].lower() == ".txt": 
    813816        onTXTSave(data, filename) 
    814     if os.path.splitext(filename)[1].lower() == ".xml": 
     817    elif os.path.splitext(filename)[1].lower() == ".xml": 
    815818        loader.save(filename, data, ".xml") 
     819    elif os.path.splitext(filename)[1].lower() == ".h5": 
     820        nxcansaswriter = NXcanSASWriter() 
     821        nxcansaswriter.write([data], filename) 
    816822 
    817823def saveData2D(data): 
     
    824830    default_name += "_out" + ext_format 
    825831 
    826     wildcard = "IGOR/DAT 2D file in Q_map (*.dat)" 
     832    wildcard = "IGOR/DAT 2D file in Q_map (*.dat);;"\ 
     833                "NXcanSAS files (*.h5)" 
    827834    kwargs = { 
    828835        'caption'   : 'Save As', 
     
    844851    if os.path.splitext(filename)[1].lower() == ext_format: 
    845852        loader.save(filename, data, ext_format) 
     853    elif os.path.splitext(filename)[1].lower() == ".h5": 
     854        nxcansaswriter = NXcanSASWriter() 
     855        nxcansaswriter.write([data], filename) 
     856 
    846857 
    847858class FormulaValidator(QtGui.QValidator): 
  • src/sas/sascalc/data_util/nxsunit.py

    r574adc7 r8db20a9  
    136136    sld = { '10^-6 Angstrom^-2': 1e-6, 'Angstrom^-2': 1 } 
    137137    Q = { 'invA': 1, 'invAng': 1, 'invAngstroms': 1, '1/A': 1, 
     138          '1/Angstrom': 1, '1/angstrom': 1, 'A^{-1}': 1, 'cm^{-1}': 1e-8, 
    138139          '10^-3 Angstrom^-1': 1e-3, '1/cm': 1e-8, '1/m': 1e-10, 
    139           'nm^-1': 0.1, '1/nm': 0.1, 'n_m^-1': 0.1 } 
     140          'nm^{-1}': 1, 'nm^-1': 0.1, '1/nm': 0.1, 'n_m^-1': 0.1 } 
    140141 
    141142    _caret_optional(sld) 
     
    157158    # units for that particular dimension. 
    158159    # Note: don't have support for dimensionless units. 
    159     unknown = {None:1, '???':1, '': 1, 'a.u.': 1} 
     160    unknown = {None:1, '???':1, '': 1, 'a.u.': 1, 'Counts': 1, 'counts': 1} 
    160161 
    161162    def __init__(self, name): 
  • src/sas/sascalc/data_util/registry.py

    ra26f67f r8db20a9  
    111111            for L in loaders: 
    112112                if L not in result: result.append(L) 
    113             loaders = L 
     113            loaders = result 
     114            #loaders = L 
    114115        # Raise an error if there are no matching extensions 
    115116        if len(loaders) == 0: 
  • src/sas/sascalc/dataloader/data_info.py

    rb8080e1 r8db20a9  
    954954        _str += "Data:\n" 
    955955        _str += "   Type:         %s\n" % self.__class__.__name__ 
    956         _str += "   X- & Y-axis:  %s\t[%s]\n" % (self._yaxis, self._yunit) 
     956        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit) 
     957        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit) 
    957958        _str += "   Z-axis:       %s\t[%s]\n" % (self._zaxis, self._zunit) 
    958959        _str += "   Length:       %g \n" % (len(self.data)) 
     
    983984                           qx_data=qx_data, qy_data=qy_data, 
    984985                           q_data=q_data, mask=mask) 
     986 
     987        clone._xaxis = self._xaxis 
     988        clone._yaxis = self._yaxis 
     989        clone._zaxis = self._zaxis 
     990        clone._xunit = self._xunit 
     991        clone._yunit = self._yunit 
     992        clone._zunit = self._zunit 
     993        clone.x_bins = self.x_bins 
     994        clone.y_bins = self.y_bins 
    985995 
    986996        clone.title = self.title 
     
    11531163def combine_data_info_with_plottable(data, datainfo): 
    11541164    """ 
    1155     A function that combines the DataInfo data in self.current_datainto with a plottable_1D or 2D data object. 
     1165    A function that combines the DataInfo data in self.current_datainto with a 
     1166    plottable_1D or 2D data object. 
    11561167 
    11571168    :param data: A plottable_1D or plottable_2D data object 
     
    11711182        final_dataset.yaxis(data._yaxis, data._yunit) 
    11721183    elif isinstance(data, plottable_2D): 
    1173         final_dataset = Data2D(data.data, data.err_data, data.qx_data, data.qy_data, data.q_data, 
    1174                                data.mask, data.dqx_data, data.dqy_data) 
     1184        final_dataset = Data2D(data.data, data.err_data, data.qx_data, 
     1185                               data.qy_data, data.q_data, data.mask, 
     1186                               data.dqx_data, data.dqy_data) 
    11751187        final_dataset.xaxis(data._xaxis, data._xunit) 
    11761188        final_dataset.yaxis(data._yaxis, data._yunit) 
    11771189        final_dataset.zaxis(data._zaxis, data._zunit) 
    1178         if len(data.data.shape) == 2: 
    1179             n_rows, n_cols = data.data.shape 
    1180             final_dataset.y_bins = data.qy_data[0::int(n_cols)] 
    1181             final_dataset.x_bins = data.qx_data[:int(n_cols)] 
     1190        final_dataset.y_bins = data.y_bins 
     1191        final_dataset.x_bins = data.x_bins 
    11821192    else: 
    1183         return_string = "Should Never Happen: _combine_data_info_with_plottable input is not a plottable1d or " + \ 
    1184                         "plottable2d data object" 
     1193        return_string = ("Should Never Happen: _combine_data_info_with_plottabl" 
     1194                         "e input is not a plottable1d or plottable2d data " 
     1195                         "object") 
    11851196        return return_string 
    11861197 
  • src/sas/sascalc/dataloader/file_reader_base_class.py

    rbe7c981 r8db20a9  
    1616from .data_info import Data1D, Data2D, DataInfo, plottable_1D, plottable_2D,\ 
    1717    combine_data_info_with_plottable 
     18from sas.sascalc.data_util.nxsunit import Converter 
    1819 
    1920logger = logging.getLogger(__name__) 
     
    3738                       "SasView cannot guarantee the accuracy of the data.") 
    3839 
     40 
    3941class FileReader(object): 
    4042    # String to describe the type of data this reader can load 
     
    4547    ext = ['.txt'] 
    4648    # Deprecated extensions 
    47     deprecated_extensions = ['.asc', '.nxs'] 
     49    deprecated_extensions = ['.asc'] 
    4850    # Bypass extension check and try to load anyway 
    4951    allow_all = False 
     
    98100                    if len(self.output) > 0: 
    99101                        # Sort the data that's been loaded 
    100                         self.sort_one_d_data() 
    101                         self.sort_two_d_data() 
     102                        self.convert_data_units() 
     103                        self.sort_data() 
    102104        else: 
    103105            msg = "Unable to find file at: {}\n".format(filepath) 
     
    140142        Returns the entire file as a string. 
    141143        """ 
    142         #return self.f_open.read() 
    143144        return decode(self.f_open.read()) 
    144145 
     
    166167        self.output.append(data_obj) 
    167168 
    168     def sort_one_d_data(self): 
     169    def sort_data(self): 
    169170        """ 
    170171        Sort 1D data along the X axis for consistency 
     
    174175                # Normalize the units for 
    175176                data.x_unit = self.format_unit(data.x_unit) 
     177                data._xunit = data.x_unit 
    176178                data.y_unit = self.format_unit(data.y_unit) 
     179                data._yunit = data.y_unit 
    177180                # Sort data by increasing x and remove 1st point 
    178181                ind = np.lexsort((data.y, data.x)) 
     
    203206                    data.ymin = np.min(data.y) 
    204207                    data.ymax = np.max(data.y) 
     208            elif isinstance(data, Data2D): 
     209                # Normalize the units for 
     210                data.Q_unit = self.format_unit(data.Q_unit) 
     211                data.I_unit = self.format_unit(data.I_unit) 
     212                data._xunit = data.Q_unit 
     213                data._yunit = data.Q_unit 
     214                data._zunit = data.I_unit 
     215                data.data = data.data.astype(np.float64) 
     216                data.qx_data = data.qx_data.astype(np.float64) 
     217                data.xmin = np.min(data.qx_data) 
     218                data.xmax = np.max(data.qx_data) 
     219                data.qy_data = data.qy_data.astype(np.float64) 
     220                data.ymin = np.min(data.qy_data) 
     221                data.ymax = np.max(data.qy_data) 
     222                data.q_data = np.sqrt(data.qx_data * data.qx_data 
     223                                         + data.qy_data * data.qy_data) 
     224                if data.err_data is not None: 
     225                    data.err_data = data.err_data.astype(np.float64) 
     226                if data.dqx_data is not None: 
     227                    data.dqx_data = data.dqx_data.astype(np.float64) 
     228                if data.dqy_data is not None: 
     229                    data.dqy_data = data.dqy_data.astype(np.float64) 
     230                if data.mask is not None: 
     231                    data.mask = data.mask.astype(dtype=bool) 
     232 
     233                if len(data.data.shape) == 2: 
     234                    n_rows, n_cols = data.data.shape 
     235                    data.y_bins = data.qy_data[0::int(n_cols)] 
     236                    data.x_bins = data.qx_data[:int(n_cols)] 
     237                    data.data = data.data.flatten() 
     238                data = self._remove_nans_in_data(data) 
     239                if len(data.data) > 0: 
     240                    data.xmin = np.min(data.qx_data) 
     241                    data.xmax = np.max(data.qx_data) 
     242                    data.ymin = np.min(data.qy_data) 
     243                    data.ymax = np.max(data.qy_data) 
    205244 
    206245    @staticmethod 
     
    242281        return data 
    243282 
    244     def sort_two_d_data(self): 
    245         for dataset in self.output: 
    246             if isinstance(dataset, Data2D): 
    247                 # Normalize the units for 
    248                 dataset.x_unit = self.format_unit(dataset.Q_unit) 
    249                 dataset.y_unit = self.format_unit(dataset.I_unit) 
    250                 dataset.data = dataset.data.astype(np.float64) 
    251                 dataset.qx_data = dataset.qx_data.astype(np.float64) 
    252                 dataset.xmin = np.min(dataset.qx_data) 
    253                 dataset.xmax = np.max(dataset.qx_data) 
    254                 dataset.qy_data = dataset.qy_data.astype(np.float64) 
    255                 dataset.ymin = np.min(dataset.qy_data) 
    256                 dataset.ymax = np.max(dataset.qy_data) 
    257                 dataset.q_data = np.sqrt(dataset.qx_data * dataset.qx_data 
    258                                          + dataset.qy_data * dataset.qy_data) 
    259                 if dataset.err_data is not None: 
    260                     dataset.err_data = dataset.err_data.astype(np.float64) 
    261                 if dataset.dqx_data is not None: 
    262                     dataset.dqx_data = dataset.dqx_data.astype(np.float64) 
    263                 if dataset.dqy_data is not None: 
    264                     dataset.dqy_data = dataset.dqy_data.astype(np.float64) 
    265                 if dataset.mask is not None: 
    266                     dataset.mask = dataset.mask.astype(dtype=bool) 
    267  
    268                 if len(dataset.data.shape) == 2: 
    269                     n_rows, n_cols = dataset.data.shape 
    270                     dataset.y_bins = dataset.qy_data[0::int(n_cols)] 
    271                     dataset.x_bins = dataset.qx_data[:int(n_cols)] 
    272                 dataset.data = dataset.data.flatten() 
    273                 dataset = self._remove_nans_in_data(dataset) 
    274                 if len(dataset.data) > 0: 
    275                     dataset.xmin = np.min(dataset.qx_data) 
    276                     dataset.xmax = np.max(dataset.qx_data) 
    277                     dataset.ymin = np.min(dataset.qy_data) 
    278                     dataset.ymax = np.max(dataset.qy_data) 
     283    @staticmethod 
     284    def set_default_1d_units(data): 
     285        """ 
     286        Set the x and y axes to the default 1D units 
     287        :param data: 1D data set 
     288        :return: 
     289        """ 
     290        data.xaxis(r"\rm{Q}", '1/A') 
     291        data.yaxis(r"\rm{Intensity}", "1/cm") 
     292        return data 
     293 
     294    @staticmethod 
     295    def set_default_2d_units(data): 
     296        """ 
     297        Set the x and y axes to the default 2D units 
     298        :param data: 2D data set 
     299        :return: 
     300        """ 
     301        data.xaxis("\\rm{Q_{x}}", '1/A') 
     302        data.yaxis("\\rm{Q_{y}}", '1/A') 
     303        data.zaxis("\\rm{Intensity}", "1/cm") 
     304        return data 
     305 
     306    def convert_data_units(self, default_q_unit="1/A"): 
     307        """ 
     308        Converts al; data to the sasview default of units of A^{-1} for Q and 
     309        cm^{-1} for I. 
     310        :param default_q_unit: The default Q unit used by Sasview 
     311        """ 
     312        convert_q = True 
     313        new_output = [] 
     314        for data in self.output: 
     315            if data.isSesans: 
     316                new_output.append(data) 
     317                continue 
     318            try: 
     319                file_x_unit = data._xunit 
     320                data_conv_x = Converter(file_x_unit) 
     321            except KeyError: 
     322                logger.info("Unrecognized Q units in data file. No data " 
     323                            "conversion attempted") 
     324                convert_q = False 
     325            try: 
     326 
     327                if isinstance(data, Data1D): 
     328                        if convert_q: 
     329                            data.x = data_conv_x(data.x, units=default_q_unit) 
     330                            data._xunit = default_q_unit 
     331                            data.x_unit = default_q_unit 
     332                            if data.dx is not None: 
     333                                data.dx = data_conv_x(data.dx, 
     334                                                      units=default_q_unit) 
     335                            if data.dxl is not None: 
     336                                data.dxl = data_conv_x(data.dxl, 
     337                                                       units=default_q_unit) 
     338                            if data.dxw is not None: 
     339                                data.dxw = data_conv_x(data.dxw, 
     340                                                       units=default_q_unit) 
     341                elif isinstance(data, Data2D): 
     342                    if convert_q: 
     343                        data.qx_data = data_conv_x(data.qx_data, 
     344                                                   units=default_q_unit) 
     345                        if data.dqx_data is not None: 
     346                            data.dqx_data = data_conv_x(data.dqx_data, 
     347                                                        units=default_q_unit) 
     348                        try: 
     349                            file_y_unit = data._yunit 
     350                            data_conv_y = Converter(file_y_unit) 
     351                            data.qy_data = data_conv_y(data.qy_data, 
     352                                                       units=default_q_unit) 
     353                            if data.dqy_data is not None: 
     354                                data.dqy_data = data_conv_y(data.dqy_data, 
     355                                                            units=default_q_unit) 
     356                        except KeyError: 
     357                            logger.info("Unrecognized Qy units in data file. No" 
     358                                        " data conversion attempted") 
     359            except KeyError: 
     360                message = "Unable to convert Q units from {0} to 1/A." 
     361                message.format(default_q_unit) 
     362                data.errors.append(message) 
     363            new_output.append(data) 
     364        self.output = new_output 
    279365 
    280366    def format_unit(self, unit=None): 
     
    367453                    self.current_dataset.qy_data)) 
    368454            if has_error_dy: 
    369                 self.current_dataset.err_data = self.current_dataset.err_data[x != 0] 
     455                self.current_dataset.err_data = self.current_dataset.err_data[ 
     456                    x != 0] 
    370457            if has_error_dqx: 
    371                 self.current_dataset.dqx_data = self.current_dataset.dqx_data[x != 0] 
     458                self.current_dataset.dqx_data = self.current_dataset.dqx_data[ 
     459                    x != 0] 
    372460            if has_error_dqy: 
    373                 self.current_dataset.dqy_data = self.current_dataset.dqy_data[x != 0] 
     461                self.current_dataset.dqy_data = self.current_dataset.dqy_data[ 
     462                    x != 0] 
    374463            if has_mask: 
    375464                self.current_dataset.mask = self.current_dataset.mask[x != 0] 
  • src/sas/sascalc/dataloader/loader.py

    rb8080e1 r8db20a9  
    367367            try: 
    368368                return fn(path, data) 
    369             except Exception: 
    370                 pass  # give other loaders a chance to succeed 
    371         # If we get here it is because all loaders failed 
    372         raise  # reraises last exception 
     369            except Exception as exc: 
     370                msg = "Saving file {} using the {} writer failed.\n".format( 
     371                    path, type(fn).__name__) 
     372                msg += str(exc) 
     373                logger.exception(msg)  # give other loaders a chance to succeed 
    373374 
    374375 
  • src/sas/sascalc/dataloader/readers/abs_reader.py

    rb8080e1 r8db20a9  
    4848        detector = Detector() 
    4949        data_line = 0 
     50        x_index = 4 
    5051        self.reset_data_list(len(lines)) 
    5152        self.current_datainfo.detector.append(detector) 
     
    6364        for line in lines: 
    6465            # Information line 1 
     66            if line.find(".bt5") > 0: 
     67                x_index = 0 
    6568            if is_info: 
    6669                is_info = False 
     
    171174 
    172175                try: 
    173                     _x = float(toks[4]) 
     176                    _x = float(toks[x_index]) 
    174177                    _y = float(toks[1]) 
    175178                    _dy = float(toks[2]) 
     
    225228            raise ValueError("ascii_reader: could not load file") 
    226229 
     230        self.current_dataset = self.set_default_1d_units(self.current_dataset) 
    227231        if data_conv_q is not None: 
    228232            self.current_dataset.xaxis("\\rm{Q}", base_q_unit) 
    229         else: 
    230             self.current_dataset.xaxis("\\rm{Q}", 'A^{-1}') 
    231233        if data_conv_i is not None: 
    232234            self.current_dataset.yaxis("\\rm{Intensity}", base_i_unit) 
    233         else: 
    234             self.current_dataset.yaxis("\\rm{Intensity}", "cm^{-1}") 
    235235 
    236236        # Store loading process information 
  • src/sas/sascalc/dataloader/readers/ascii_reader.py

    r9e6aeaf r8db20a9  
    157157 
    158158        self.remove_empty_q_values() 
    159         self.current_dataset.xaxis("\\rm{Q}", 'A^{-1}') 
    160         self.current_dataset.yaxis("\\rm{Intensity}", "cm^{-1}") 
     159        self.current_dataset = self.set_default_1d_units(self.current_dataset) 
    161160 
    162161        # Store loading process information 
  • src/sas/sascalc/dataloader/readers/associations.py

    rb8080e1 r8db20a9  
    2323    ".ses": "sesans_reader", 
    2424    ".h5": "cansas_reader_HDF5", 
     25    ".nxs": "cansas_reader_HDF5", 
    2526    ".txt": "ascii_reader", 
    2627    ".dat": "red2d_reader", 
  • src/sas/sascalc/dataloader/readers/cansas_reader.py

    rb1b71ad r8db20a9  
    812812            node.append(point) 
    813813            self.write_node(point, "Q", datainfo.x[i], 
    814                             {'unit': datainfo.x_unit}) 
     814                            {'unit': datainfo._xunit}) 
    815815            if len(datainfo.y) >= i: 
    816816                self.write_node(point, "I", datainfo.y[i], 
    817                                 {'unit': datainfo.y_unit}) 
     817                                {'unit': datainfo._yunit}) 
    818818            if datainfo.dy is not None and len(datainfo.dy) > i: 
    819819                self.write_node(point, "Idev", datainfo.dy[i], 
    820                                 {'unit': datainfo.y_unit}) 
     820                                {'unit': datainfo._yunit}) 
    821821            if datainfo.dx is not None and len(datainfo.dx) > i: 
    822822                self.write_node(point, "Qdev", datainfo.dx[i], 
    823                                 {'unit': datainfo.x_unit}) 
     823                                {'unit': datainfo._xunit}) 
    824824            if datainfo.dxw is not None and len(datainfo.dxw) > i: 
    825825                self.write_node(point, "dQw", datainfo.dxw[i], 
    826                                 {'unit': datainfo.x_unit}) 
     826                                {'unit': datainfo._xunit}) 
    827827            if datainfo.dxl is not None and len(datainfo.dxl) > i: 
    828828                self.write_node(point, "dQl", datainfo.dxl[i], 
    829                                 {'unit': datainfo.x_unit}) 
     829                                {'unit': datainfo._xunit}) 
    830830        if datainfo.isSesans: 
    831831            sesans_attrib = {'x_axis': datainfo._xaxis, 
  • src/sas/sascalc/dataloader/readers/cansas_reader_HDF5.py

    rb8080e1 r8db20a9  
    11""" 
    2     CanSAS 2D data reader for reading HDF5 formatted CanSAS files. 
     2    NXcanSAS data reader for reading HDF5 formatted CanSAS files. 
    33""" 
    44 
     
    1212    Data1D, Data2D, DataInfo, Process, Aperture, Collimation, \ 
    1313    TransmissionSpectrum, Detector 
    14 from ..data_info import combine_data_info_with_plottable 
    1514from ..loader_exceptions import FileContentsException, DefaultReaderException 
    1615from ..file_reader_base_class import FileReader, decode 
    1716 
     17try: 
     18  basestring 
     19except NameError:  # CRUFT: python 2 support 
     20  basestring = str 
     21 
     22 
    1823def h5attr(node, key, default=None): 
    1924    return decode(node.attrs.get(key, default)) 
    2025 
     26 
    2127class Reader(FileReader): 
    2228    """ 
    23     A class for reading in CanSAS v2.0 data files. The existing iteration opens 
    24     Mantid generated HDF5 formatted files with file extension .h5/.H5. Any 
    25     number of data sets may be present within the file and any dimensionality 
    26     of data may be used. Currently 1D and 2D SAS data sets are supported, but 
    27     future implementations will include 1D and 2D SESANS data. 
    28  
    29     Any number of SASdata sets may be present in a SASentry and the data within 
    30     can be either 1D I(Q) or 2D I(Qx, Qy). 
    31  
    32     Also supports reading NXcanSAS formatted HDF5 files 
     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). 
    3339 
    3440    :Dependencies: 
    35         The CanSAS HDF5 reader requires h5py => v2.5.0 or later. 
     41        The NXcanSAS HDF5 reader requires h5py => v2.5.0 or later. 
    3642    """ 
    3743 
    3844    # CanSAS version 
    3945    cansas_version = 2.0 
    40     # Logged warnings or messages 
    41     logging = None 
    42     # List of errors for the current data set 
    43     errors = None 
    44     # Raw file contents to be processed 
    45     raw_data = None 
    46     # List of plottable1D objects that should be linked to the current_datainfo 
    47     data1d = None 
    48     # List of plottable2D objects that should be linked to the current_datainfo 
    49     data2d = None 
    5046    # Data type name 
    51     type_name = "CanSAS 2.0" 
     47    type_name = "NXcanSAS" 
    5248    # Wildcards 
    53     type = ["CanSAS 2.0 HDF5 Files (*.h5)|*.h5"] 
     49    type = ["NXcanSAS HDF5 Files (*.h5)|*.h5|"] 
    5450    # List of allowed extensions 
    5551    ext = ['.h5', '.H5'] 
     
    8177                except Exception as e: 
    8278                    if extension not in self.ext: 
    83                         msg = "CanSAS2.0 HDF5 Reader could not load file {}".format(basename + extension) 
     79                        msg = "NXcanSAS Reader could not load file {}".format( 
     80                            basename + extension) 
    8481                        raise DefaultReaderException(msg) 
    8582                    raise FileContentsException(e.message) 
     
    9592                    self.raw_data.close() 
    9693 
    97                 for dataset in self.output: 
    98                     if isinstance(dataset, Data1D): 
    99                         if dataset.x.size < 5: 
    100                             self.output = [] 
    101                             raise FileContentsException("Fewer than 5 data points found.") 
     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) 
    102100 
    103101    def reset_state(self): 
     
    109107        self.data2d = [] 
    110108        self.raw_data = None 
    111         self.errors = set() 
     109        self.multi_frame = False 
     110        self.data_frames = [] 
     111        self.data_uncertainty_frames = [] 
     112        self.errors = [] 
    112113        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 = [] 
    113121        self.parent_class = u'' 
    114122        self.detector = Detector() 
     
    131139            value = data.get(key) 
    132140            class_name = h5attr(value, u'canSAS_class') 
     141            if isinstance(class_name, (list, tuple, np.ndarray)): 
     142                class_name = class_name[0] 
    133143            if class_name is None: 
    134144                class_name = h5attr(value, u'NX_class') 
     
    140150            if isinstance(value, h5py.Group): 
    141151                # Set parent class before recursion 
     152                last_parent_class = self.parent_class 
    142153                self.parent_class = class_name 
    143154                parent_list.append(key) 
     
    147158                    self.add_data_set(key) 
    148159                elif class_prog.match(u'SASdata'): 
    149                     self._initialize_new_data_set(parent_list) 
     160                    self._find_data_attributes(value) 
     161                    self._initialize_new_data_set(value) 
    150162                # Recursion step to access data within the group 
    151163                self.read_children(value, parent_list) 
     164                self.add_intermediate() 
    152165                # Reset parent class when returning from recursive method 
    153                 self.parent_class = class_name 
    154                 self.add_intermediate() 
     166                self.parent_class = last_parent_class 
    155167                parent_list.remove(key) 
    156168 
    157169            elif isinstance(value, h5py.Dataset): 
    158170                # If this is a dataset, store the data appropriately 
    159                 data_set = data[key][:] 
     171                data_set = value.value 
    160172                unit = self._get_unit(value) 
    161  
    162                 # I and Q Data 
    163                 if key == u'I': 
    164                     if isinstance(self.current_dataset, plottable_2D): 
    165                         self.current_dataset.data = data_set 
    166                         self.current_dataset.zaxis("Intensity", unit) 
    167                     else: 
    168                         self.current_dataset.y = data_set.flatten() 
    169                         self.current_dataset.yaxis("Intensity", unit) 
    170                     continue 
    171                 elif key == u'Idev': 
    172                     if isinstance(self.current_dataset, plottable_2D): 
    173                         self.current_dataset.err_data = data_set.flatten() 
    174                     else: 
    175                         self.current_dataset.dy = data_set.flatten() 
    176                     continue 
    177                 elif key == u'Q': 
    178                     self.current_dataset.xaxis("Q", unit) 
    179                     if isinstance(self.current_dataset, plottable_2D): 
    180                         self.current_dataset.q = data_set.flatten() 
    181                     else: 
    182                         self.current_dataset.x = data_set.flatten() 
    183                     continue 
    184                 elif key == u'Qdev': 
    185                     self.current_dataset.dx = data_set.flatten() 
    186                     continue 
    187                 elif key == u'dQw': 
    188                     self.current_dataset.dxw = data_set.flatten() 
    189                     continue 
    190                 elif key == u'dQl': 
    191                     self.current_dataset.dxl = data_set.flatten() 
    192                     continue 
    193                 elif key == u'Qy': 
    194                     self.current_dataset.yaxis("Q_y", unit) 
    195                     self.current_dataset.qy_data = data_set.flatten() 
    196                     continue 
    197                 elif key == u'Qydev': 
    198                     self.current_dataset.dqy_data = data_set.flatten() 
    199                     continue 
    200                 elif key == u'Qx': 
    201                     self.current_dataset.xaxis("Q_x", unit) 
    202                     self.current_dataset.qx_data = data_set.flatten() 
    203                     continue 
    204                 elif key == u'Qxdev': 
    205                     self.current_dataset.dqx_data = data_set.flatten() 
    206                     continue 
    207                 elif key == u'Mask': 
    208                     self.current_dataset.mask = data_set.flatten() 
    209                     continue 
    210                 # Transmission Spectrum 
    211                 elif (key == u'T' 
    212                       and self.parent_class == u'SAStransmission_spectrum'): 
    213                     self.trans_spectrum.transmission = data_set.flatten() 
    214                     continue 
    215                 elif (key == u'Tdev' 
    216                       and self.parent_class == u'SAStransmission_spectrum'): 
    217                     self.trans_spectrum.transmission_deviation = \ 
    218                         data_set.flatten() 
    219                     continue 
    220                 elif (key == u'lambda' 
    221                       and self.parent_class == u'SAStransmission_spectrum'): 
    222                     self.trans_spectrum.wavelength = data_set.flatten() 
    223                     continue 
    224173 
    225174                for data_point in data_set: 
     
    231180                    # Top Level Meta Data 
    232181                    if key == u'definition': 
    233                         self.current_datainfo.meta_data['reader'] = data_point 
     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 
    234189                    elif key == u'run': 
    235                         self.current_datainfo.run.append(data_point) 
    236190                        try: 
    237191                            run_name = h5attr(value, 'name') 
    238                             run_dict = {data_point: run_name} 
     192                            run_dict = {data_set: run_name} 
    239193                            self.current_datainfo.run_name = run_dict 
    240194                        except Exception: 
    241195                            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 
    242202                    elif key == u'title': 
    243                         self.current_datainfo.title = data_point 
     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 
    244209                    elif key == u'SASnote': 
    245                         self.current_datainfo.notes.append(data_point) 
    246  
     210                        self.current_datainfo.notes.append(data_set) 
     211                        break 
    247212                    # Sample Information 
    248                     # CanSAS 2.0 format 
    249                     elif key == u'Title' and self.parent_class == u'SASsample': 
    250                         self.current_datainfo.sample.name = data_point 
    251                     # NXcanSAS format 
    252                     elif key == u'name' and self.parent_class == u'SASsample': 
    253                         self.current_datainfo.sample.name = data_point 
    254                     # NXcanSAS format 
    255                     elif key == u'ID' and self.parent_class == u'SASsample': 
    256                         self.current_datainfo.sample.name = data_point 
    257                     elif (key == u'thickness' 
    258                           and self.parent_class == u'SASsample'): 
    259                         self.current_datainfo.sample.thickness = data_point 
    260                     elif (key == u'temperature' 
    261                           and self.parent_class == u'SASsample'): 
    262                         self.current_datainfo.sample.temperature = data_point 
    263                     elif (key == u'transmission' 
    264                           and self.parent_class == u'SASsample'): 
    265                         self.current_datainfo.sample.transmission = data_point 
    266                     elif (key == u'x_position' 
    267                           and self.parent_class == u'SASsample'): 
    268                         self.current_datainfo.sample.position.x = data_point 
    269                     elif (key == u'y_position' 
    270                           and self.parent_class == u'SASsample'): 
    271                         self.current_datainfo.sample.position.y = data_point 
    272                     elif key == u'pitch' and self.parent_class == u'SASsample': 
    273                         self.current_datainfo.sample.orientation.x = data_point 
    274                     elif key == u'yaw' and self.parent_class == u'SASsample': 
    275                         self.current_datainfo.sample.orientation.y = data_point 
    276                     elif key == u'roll' and self.parent_class == u'SASsample': 
    277                         self.current_datainfo.sample.orientation.z = data_point 
    278                     elif (key == u'details' 
    279                           and self.parent_class == u'SASsample'): 
    280                         self.current_datainfo.sample.details.append(data_point) 
    281  
     213                    elif self.parent_class == u'SASsample': 
     214                        self.process_sample(data_point, key) 
    282215                    # Instrumental Information 
    283216                    elif (key == u'name' 
    284217                          and self.parent_class == u'SASinstrument'): 
    285218                        self.current_datainfo.instrument = data_point 
    286                     elif key == u'name' and self.parent_class == u'SASdetector': 
    287                         self.detector.name = data_point 
    288                     elif key == u'SDD' and self.parent_class == u'SASdetector': 
    289                         self.detector.distance = float(data_point) 
    290                         self.detector.distance_unit = unit 
    291                     elif (key == u'slit_length' 
    292                           and self.parent_class == u'SASdetector'): 
    293                         self.detector.slit_length = float(data_point) 
    294                         self.detector.slit_length_unit = unit 
    295                     elif (key == u'x_position' 
    296                           and self.parent_class == u'SASdetector'): 
    297                         self.detector.offset.x = float(data_point) 
    298                         self.detector.offset_unit = unit 
    299                     elif (key == u'y_position' 
    300                           and self.parent_class == u'SASdetector'): 
    301                         self.detector.offset.y = float(data_point) 
    302                         self.detector.offset_unit = unit 
    303                     elif (key == u'pitch' 
    304                           and self.parent_class == u'SASdetector'): 
    305                         self.detector.orientation.x = float(data_point) 
    306                         self.detector.orientation_unit = unit 
    307                     elif key == u'roll' and self.parent_class == u'SASdetector': 
    308                         self.detector.orientation.z = float(data_point) 
    309                         self.detector.orientation_unit = unit 
    310                     elif key == u'yaw' and self.parent_class == u'SASdetector': 
    311                         self.detector.orientation.y = float(data_point) 
    312                         self.detector.orientation_unit = unit 
    313                     elif (key == u'beam_center_x' 
    314                           and self.parent_class == u'SASdetector'): 
    315                         self.detector.beam_center.x = float(data_point) 
    316                         self.detector.beam_center_unit = unit 
    317                     elif (key == u'beam_center_y' 
    318                           and self.parent_class == u'SASdetector'): 
    319                         self.detector.beam_center.y = float(data_point) 
    320                         self.detector.beam_center_unit = unit 
    321                     elif (key == u'x_pixel_size' 
    322                           and self.parent_class == u'SASdetector'): 
    323                         self.detector.pixel_size.x = float(data_point) 
    324                         self.detector.pixel_size_unit = unit 
    325                     elif (key == u'y_pixel_size' 
    326                           and self.parent_class == u'SASdetector'): 
    327                         self.detector.pixel_size.y = float(data_point) 
    328                         self.detector.pixel_size_unit = unit 
    329                     elif (key == u'distance' 
    330                           and self.parent_class == u'SAScollimation'): 
    331                         self.collimation.length = data_point 
    332                         self.collimation.length_unit = unit 
    333                     elif (key == u'name' 
    334                           and self.parent_class == u'SAScollimation'): 
    335                         self.collimation.name = data_point 
    336                     elif (key == u'shape' 
    337                           and self.parent_class == u'SASaperture'): 
    338                         self.aperture.shape = data_point 
    339                     elif (key == u'x_gap' 
    340                           and self.parent_class == u'SASaperture'): 
    341                         self.aperture.size.x = data_point 
    342                     elif (key == u'y_gap' 
    343                           and self.parent_class == u'SASaperture'): 
    344                         self.aperture.size.y = data_point 
    345  
     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) 
    346228                    # Process Information 
    347                     elif (key == u'Title' 
    348                           and self.parent_class == u'SASprocess'): # CanSAS 2.0 
    349                         self.process.name = data_point 
    350                     elif (key == u'name' 
    351                           and self.parent_class == u'SASprocess'): # NXcanSAS 
    352                         self.process.name = data_point 
    353                     elif (key == u'description' 
    354                           and self.parent_class == u'SASprocess'): 
    355                         self.process.description = data_point 
    356                     elif key == u'date' and self.parent_class == u'SASprocess': 
    357                         self.process.date = data_point 
    358                     elif key == u'term' and self.parent_class == u'SASprocess': 
    359                         self.process.term = data_point 
    360                     elif self.parent_class == u'SASprocess': 
    361                         self.process.notes.append(data_point) 
    362  
     229                    elif self.parent_class == u'SASprocess': # CanSAS 2.0 
     230                        self.process_process(data_point, key) 
    363231                    # Source 
    364                     elif (key == u'wavelength' 
    365                           and self.parent_class == u'SASdata'): 
    366                         self.current_datainfo.source.wavelength = data_point 
    367                         self.current_datainfo.source.wavelength_unit = unit 
    368                     elif (key == u'incident_wavelength' 
    369                           and self.parent_class == 'SASsource'): 
    370                         self.current_datainfo.source.wavelength = data_point 
    371                         self.current_datainfo.source.wavelength_unit = unit 
    372                     elif (key == u'wavelength_max' 
    373                           and self.parent_class == u'SASsource'): 
    374                         self.current_datainfo.source.wavelength_max = data_point 
    375                         self.current_datainfo.source.wavelength_max_unit = unit 
    376                     elif (key == u'wavelength_min' 
    377                           and self.parent_class == u'SASsource'): 
    378                         self.current_datainfo.source.wavelength_min = data_point 
    379                         self.current_datainfo.source.wavelength_min_unit = unit 
    380                     elif (key == u'incident_wavelength_spread' 
    381                           and self.parent_class == u'SASsource'): 
    382                         self.current_datainfo.source.wavelength_spread = \ 
    383                             data_point 
    384                         self.current_datainfo.source.wavelength_spread_unit = \ 
    385                             unit 
    386                     elif (key == u'beam_size_x' 
    387                           and self.parent_class == u'SASsource'): 
    388                         self.current_datainfo.source.beam_size.x = data_point 
    389                         self.current_datainfo.source.beam_size_unit = unit 
    390                     elif (key == u'beam_size_y' 
    391                           and self.parent_class == u'SASsource'): 
    392                         self.current_datainfo.source.beam_size.y = data_point 
    393                         self.current_datainfo.source.beam_size_unit = unit 
    394                     elif (key == u'beam_shape' 
    395                           and self.parent_class == u'SASsource'): 
    396                         self.current_datainfo.source.beam_shape = data_point 
    397                     elif (key == u'radiation' 
    398                           and self.parent_class == u'SASsource'): 
    399                         self.current_datainfo.source.radiation = data_point 
    400                     elif (key == u'transmission' 
    401                           and self.parent_class == u'SASdata'): 
    402                         self.current_datainfo.sample.transmission = data_point 
    403  
     232                    elif self.parent_class == u'SASsource': 
     233                        self.process_source(data_point, key, unit) 
    404234                    # 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 
    405245                    else: 
    406246                        new_key = self._create_unique_key( 
     
    410250            else: 
    411251                # I don't know if this reachable code 
    412                 self.errors.add("ShouldNeverHappenException") 
     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) 
    413505 
    414506    def add_intermediate(self): 
     
    440532                self.data2d.append(self.current_dataset) 
    441533            elif isinstance(self.current_dataset, plottable_1D): 
    442                 self.data1d.append(self.current_dataset) 
     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) 
    443543 
    444544    def final_data_cleanup(self): 
     
    452552            spectrum_list = [] 
    453553            for spectrum in self.current_datainfo.trans_spectrum: 
    454                 spectrum.transmission = np.delete(spectrum.transmission, [0]) 
    455554                spectrum.transmission = spectrum.transmission.astype(np.float64) 
    456                 spectrum.transmission_deviation = np.delete( 
    457                     spectrum.transmission_deviation, [0]) 
    458555                spectrum.transmission_deviation = \ 
    459556                    spectrum.transmission_deviation.astype(np.float64) 
    460                 spectrum.wavelength = np.delete(spectrum.wavelength, [0]) 
    461557                spectrum.wavelength = spectrum.wavelength.astype(np.float64) 
    462558                if len(spectrum.transmission) > 0: 
     
    466562        # Append errors to dataset and reset class errors 
    467563        self.current_datainfo.errors = self.errors 
    468         self.errors.clear() 
     564        self.errors = [] 
    469565 
    470566        # Combine all plottables with datainfo and append each to output 
    471567        # Type cast data arrays to float64 and find min/max as appropriate 
    472568        for dataset in self.data2d: 
    473             zeros = np.ones(dataset.data.size, dtype=bool) 
    474             try: 
    475                 for i in range(0, dataset.mask.size - 1): 
    476                     zeros[i] = dataset.mask[i] 
    477             except: 
    478                 self.errors.add(sys.exc_value) 
    479             dataset.mask = zeros 
    480569            # Calculate the actual Q matrix 
    481570            try: 
     
    484573                                             * dataset.qx_data 
    485574                                             + dataset.qy_data 
    486                                              * dataset.qy_data) 
     575                                             * dataset.qy_data).flatten() 
    487576            except: 
    488577                dataset.q_data = None 
    489578 
    490579            if dataset.data.ndim == 2: 
    491                 (n_rows, n_cols) = dataset.data.shape 
    492                 dataset.y_bins = dataset.qy_data[0::n_cols] 
    493                 dataset.x_bins = dataset.qx_data[:n_cols] 
     580                dataset.y_bins = np.unique(dataset.qy_data.flatten()) 
     581                dataset.x_bins = np.unique(dataset.qx_data.flatten()) 
    494582                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) 
    495591            self.current_dataset = dataset 
    496592            self.send_to_output() 
     
    511607        if self.current_datainfo and self.current_dataset: 
    512608            self.final_data_cleanup() 
     609        self.data_frames = [] 
     610        self.data_uncertainty_frames = [] 
    513611        self.data1d = [] 
    514612        self.data2d = [] 
    515613        self.current_datainfo = DataInfo() 
    516614 
    517  
    518     def _initialize_new_data_set(self, parent_list=None): 
     615    def _initialize_new_data_set(self, value=None): 
    519616        """ 
    520617        A private class method to generate a new 1D or 2D data object based on 
     
    524621        :param parent_list: List of names of parent elements 
    525622        """ 
    526  
    527         if parent_list is None: 
    528             parent_list = [] 
    529         if self._find_intermediate(parent_list, "Qx"): 
     623        if self._is_2d_not_multi_frame(value): 
    530624            self.current_dataset = plottable_2D() 
    531625        else: 
     
    535629        self.current_datainfo.filename = self.raw_data.filename 
    536630 
    537     def _find_intermediate(self, parent_list, basename=""): 
    538         """ 
    539         A private class used to find an entry by either using a direct key or 
    540         knowing the approximate basename. 
    541  
    542         :param parent_list: List of parents nodes in the HDF5 file 
     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        if isinstance(signal, bytes): 
     661            signal = signal.decode() 
     662        i_axes = attrs.get("I_axes", ["Q"]) 
     663        if isinstance(i_axes, bytes): 
     664            i_axes = i_axes.decode() 
     665        q_indices = attrs.get("Q_indices", [0]) 
     666        if isinstance(q_indices, bytes): 
     667            q_indices = q_indices.decode() 
     668        i_axes = self.as_list_or_array(i_axes) 
     669        keys = value.keys() 
     670        # Assign attributes to appropriate class variables 
     671        self.q_names = [i_axes[int(v)] for v in self.as_list_or_array(q_indices)] 
     672        self.mask_name = attrs.get("mask") 
     673        if isinstance(self.mask_name, bytes): 
     674            self.mask_name = self.mask_name.decode() 
     675        self.i_name = signal 
     676        self.i_node = value.get(self.i_name) 
     677 
     678        for item in self.q_names: 
     679            if item in keys: 
     680                q_vals = value.get(item) 
     681                if q_vals.attrs.get("uncertainties") is not None: 
     682                    self.q_uncertainty_names = q_vals.attrs.get("uncertainties") 
     683                elif q_vals.attrs.get("uncertainty") is not None: 
     684                    self.q_uncertainty_names = q_vals.attrs.get("uncertainty") 
     685                if isinstance(self.q_uncertainty_names, basestring): 
     686                    self.q_uncertainty_names = self.q_uncertainty_names.split(",") 
     687                if q_vals.attrs.get("resolutions") is not None: 
     688                    self.q_resolution_names = q_vals.attrs.get("resolutions") 
     689                if isinstance(self.q_resolution_names, basestring): 
     690                    self.q_resolution_names = self.q_resolution_names.split(",") 
     691        if self.i_name in keys: 
     692            i_vals = value.get(self.i_name) 
     693            self.i_uncertainties_name = i_vals.attrs.get("uncertainties") 
     694            if self.i_uncertainties_name is None: 
     695                self.i_uncertainties_name = i_vals.attrs.get("uncertainty") 
     696            if isinstance(self.i_uncertainties_name, bytes): 
     697                key = self.i_uncertainties_name 
     698                self.i_uncertainties_name = key.decode() if isinstance(key, bytes) else key 
     699 
     700    def _is_2d_not_multi_frame(self, value, i_base="", q_base=""): 
     701        """ 
     702        A private class to determine if the data set is 1d or 2d. 
     703 
     704        :param value: Nexus/NXcanSAS data group 
    543705        :param basename: Approximate name of an entry to search for 
    544         :return: 
    545         """ 
    546  
    547         entry = False 
    548         key_prog = re.compile(basename) 
    549         top = self.raw_data 
    550         for parent in parent_list: 
    551             top = top.get(parent) 
    552         for key in top.keys(): 
    553             if key_prog.match(key): 
    554                 entry = True 
    555                 break 
    556         return entry 
     706        :return: True if 2D, otherwise false 
     707        """ 
     708        i_basename = i_base if i_base != "" else self.i_name 
     709        i_vals = value.get(i_basename) 
     710        q_basename = q_base if q_base != "" else self.q_names 
     711        q_vals = value.get(q_basename[0]) 
     712        self.multi_frame = (i_vals is not None and q_vals is not None 
     713                            and len(i_vals.shape) != 1 
     714                            and len(q_vals.shape) == 1) 
     715        return (i_vals is not None and len(i_vals.shape) != 1 
     716                and not self.multi_frame) 
    557717 
    558718    def _create_unique_key(self, dictionary, name, numb=0): 
     
    583743        if unit is None: 
    584744            unit = h5attr(value, u'unit') 
    585         # Convert the unit formats 
    586         if unit == "1/A": 
    587             unit = "A^{-1}" 
    588         elif unit == "1/cm": 
    589             unit = "cm^{-1}" 
    590745        return unit 
  • src/sas/sascalc/dataloader/readers/danse_reader.py

    rb8080e1 r8db20a9  
    180180        detector.beam_center.y = center_y * pixel 
    181181 
    182  
    183         self.current_dataset.xaxis("\\rm{Q_{x}}", 'A^{-1}') 
    184         self.current_dataset.yaxis("\\rm{Q_{y}}", 'A^{-1}') 
    185         self.current_dataset.zaxis("\\rm{Intensity}", "cm^{-1}") 
    186  
     182        self.current_dataset = self.set_default_2d_units(self.current_dataset) 
    187183        self.current_dataset.x_bins = x_vals 
    188184        self.current_dataset.y_bins = y_vals 
  • src/sas/sascalc/dataloader/readers/red2d_reader.py

    rc8321cfc r8db20a9  
    317317 
    318318        # Units of axes 
    319         self.current_dataset.xaxis(r"\rm{Q_{x}}", 'A^{-1}') 
    320         self.current_dataset.yaxis(r"\rm{Q_{y}}", 'A^{-1}') 
    321         self.current_dataset.zaxis(r"\rm{Intensity}", "cm^{-1}") 
     319        self.current_dataset = self.set_default_2d_units(self.current_dataset) 
    322320 
    323321        # Store loading process information 
  • src/sas/sascalc/file_converter/nxcansas_writer.py

    r574adc7 r8db20a9  
    88import os 
    99 
    10 from sas.sascalc.dataloader.readers.cansas_reader_HDF5 import Reader as Cansas2Reader 
     10from sas.sascalc.dataloader.readers.cansas_reader_HDF5 import Reader 
    1111from sas.sascalc.dataloader.data_info import Data1D, Data2D 
    1212 
    13 class NXcanSASWriter(Cansas2Reader): 
     13class NXcanSASWriter(Reader): 
    1414    """ 
    1515    A class for writing in NXcanSAS data files. Any number of data sets may be 
     
    8787                    entry[names[2]].attrs['units'] = units 
    8888 
    89         valid_data = all([issubclass(d.__class__, (Data1D, Data2D)) for d in dataset]) 
     89        valid_data = all([isinstance(d, (Data1D, Data2D)) for d in dataset]) 
    9090        if not valid_data: 
    91             raise ValueError("All entries of dataset must be Data1D or Data2D objects") 
     91            raise ValueError("All entries of dataset must be Data1D or Data2D" 
     92                             "objects") 
    9293 
    9394        # Get run name and number from first Data object 
     
    109110        sasentry.attrs['version'] = '1.0' 
    110111 
    111         i = 1 
    112  
    113         for data_obj in dataset: 
    114             data_entry = sasentry.create_group("sasdata{0:0=2d}".format(i)) 
     112        for i, data_obj in enumerate(dataset): 
     113            data_entry = sasentry.create_group("sasdata{0:0=2d}".format(i+1)) 
    115114            data_entry.attrs['canSAS_class'] = 'SASdata' 
    116115            if isinstance(data_obj, Data1D): 
     
    118117            elif isinstance(data_obj, Data2D): 
    119118                self._write_2d_data(data_obj, data_entry) 
    120             i += 1 
    121119 
    122120        data_info = dataset[0] 
     
    148146                sample_entry.create_dataset('details', data=details) 
    149147 
    150         # Instrumment metadata 
     148        # Instrument metadata 
    151149        instrument_entry = sasentry.create_group('sasinstrument') 
    152150        instrument_entry.attrs['canSAS_class'] = 'SASinstrument' 
     
    176174            units=data_info.source.beam_size_unit, write_fn=_write_h5_float) 
    177175 
    178  
    179176        # Collimation metadata 
    180177        if len(data_info.collimation) > 0: 
    181             i = 1 
    182             for coll_info in data_info.collimation: 
     178            for i, coll_info in enumerate(data_info.collimation): 
    183179                collimation_entry = instrument_entry.create_group( 
    184                     'sascollimation{0:0=2d}'.format(i)) 
     180                    'sascollimation{0:0=2d}'.format(i + 1)) 
    185181                collimation_entry.attrs['canSAS_class'] = 'SAScollimation' 
    186182                if coll_info.length is not None: 
    187183                    _write_h5_float(collimation_entry, coll_info.length, 'SDD') 
    188                     collimation_entry['SDD'].attrs['units'] = coll_info.length_unit 
     184                    collimation_entry['SDD'].attrs['units'] =\ 
     185                        coll_info.length_unit 
    189186                if coll_info.name is not None: 
    190187                    collimation_entry['name'] = _h5_string(coll_info.name) 
    191188        else: 
    192             # Create a blank one - at least 1 set of collimation metadata 
    193             # required by format 
    194             collimation_entry = instrument_entry.create_group('sascollimation01') 
     189            # Create a blank one - at least 1 collimation required by format 
     190            instrument_entry.create_group('sascollimation01') 
    195191 
    196192        # Detector metadata 
    197193        if len(data_info.detector) > 0: 
    198194            i = 1 
    199             for det_info in data_info.detector: 
     195            for i, det_info in enumerate(data_info.detector): 
    200196                detector_entry = instrument_entry.create_group( 
    201                     'sasdetector{0:0=2d}'.format(i)) 
     197                    'sasdetector{0:0=2d}'.format(i + 1)) 
    202198                detector_entry.attrs['canSAS_class'] = 'SASdetector' 
    203199                if det_info.distance is not None: 
    204200                    _write_h5_float(detector_entry, det_info.distance, 'SDD') 
    205                     detector_entry['SDD'].attrs['units'] = det_info.distance_unit 
     201                    detector_entry['SDD'].attrs['units'] =\ 
     202                        det_info.distance_unit 
    206203                if det_info.name is not None: 
    207204                    detector_entry['name'] = _h5_string(det_info.name) 
     
    209206                    detector_entry['name'] = _h5_string('') 
    210207                if det_info.slit_length is not None: 
    211                     _write_h5_float(detector_entry, det_info.slit_length, 'slit_length') 
    212                     detector_entry['slit_length'].attrs['units'] = det_info.slit_length_unit 
     208                    _write_h5_float(detector_entry, det_info.slit_length, 
     209                                    'slit_length') 
     210                    detector_entry['slit_length'].attrs['units'] =\ 
     211                        det_info.slit_length_unit 
    213212                _write_h5_vector(detector_entry, det_info.offset) 
    214213                # NXcanSAS doesn't save information about pitch, only roll 
     
    224223                    names=['x_pixel_size', 'y_pixel_size'], 
    225224                    write_fn=_write_h5_float, units=det_info.pixel_size_unit) 
    226  
    227                 i += 1 
    228225        else: 
    229226            # Create a blank one - at least 1 detector required by format 
     
    231228            detector_entry.attrs['canSAS_class'] = 'SASdetector' 
    232229            detector_entry.attrs['name'] = '' 
     230 
     231        # Process meta data 
     232        for i, process in enumerate(data_info.process): 
     233            process_entry = sasentry.create_group('sasprocess{0:0=2d}'.format( 
     234                i + 1)) 
     235            process_entry.attrs['canSAS_class'] = 'SASprocess' 
     236            if process.name: 
     237                name = _h5_string(process.name) 
     238                process_entry.create_dataset('name', data=name) 
     239            if process.date: 
     240                date = _h5_string(process.date) 
     241                process_entry.create_dataset('date', data=date) 
     242            if process.description: 
     243                desc = _h5_string(process.description) 
     244                process_entry.create_dataset('description', data=desc) 
     245            for j, term in enumerate(process.term): 
     246                # Don't save empty terms 
     247                if term: 
     248                    h5_term = _h5_string(term) 
     249                    process_entry.create_dataset('term{0:0=2d}'.format( 
     250                        j + 1), data=h5_term) 
     251            for j, note in enumerate(process.notes): 
     252                # Don't save empty notes 
     253                if note: 
     254                    h5_note = _h5_string(note) 
     255                    process_entry.create_dataset('note{0:0=2d}'.format( 
     256                        j + 1), data=h5_note) 
     257 
     258        # Transmission Spectrum 
     259        for i, trans in enumerate(data_info.trans_spectrum): 
     260            trans_entry = sasentry.create_group( 
     261                'sastransmission_spectrum{0:0=2d}'.format(i + 1)) 
     262            trans_entry.attrs['canSAS_class'] = 'SAStransmission_spectrum' 
     263            trans_entry.attrs['signal'] = 'T' 
     264            trans_entry.attrs['T_axes'] = 'T' 
     265            trans_entry.attrs['name'] = trans.name 
     266            if trans.timestamp is not '': 
     267                trans_entry.attrs['timestamp'] = trans.timestamp 
     268            transmission = trans_entry.create_dataset('T', 
     269                                                      data=trans.transmission) 
     270            transmission.attrs['unertainties'] = 'Tdev' 
     271            trans_entry.create_dataset('Tdev', 
     272                                       data=trans.transmission_deviation) 
     273            trans_entry.create_dataset('lambda', data=trans.wavelength) 
    233274 
    234275        note_entry = sasentry.create_group('sasnote'.format(i)) 
     
    254295        data_entry.attrs['signal'] = 'I' 
    255296        data_entry.attrs['I_axes'] = 'Q' 
    256         data_entry.attrs['I_uncertainties'] = 'Idev' 
    257         data_entry.attrs['Q_indicies'] = 0 
    258  
    259         dI = data_obj.dy 
    260         if dI is None: 
    261             dI = np.zeros((data_obj.y.shape)) 
    262  
    263         data_entry.create_dataset('Q', data=data_obj.x) 
    264         data_entry.create_dataset('I', data=data_obj.y) 
    265         data_entry.create_dataset('Idev', data=dI) 
     297        data_entry.attrs['Q_indices'] = [0] 
     298        q_entry = data_entry.create_dataset('Q', data=data_obj.x) 
     299        q_entry.attrs['units'] = data_obj.x_unit 
     300        i_entry = data_entry.create_dataset('I', data=data_obj.y) 
     301        i_entry.attrs['units'] = data_obj.y_unit 
     302        if data_obj.dy is not None: 
     303            i_entry.attrs['uncertainties'] = 'Idev' 
     304            i_dev_entry = data_entry.create_dataset('Idev', data=data_obj.dy) 
     305            i_dev_entry.attrs['units'] = data_obj.y_unit 
     306        if data_obj.dx is not None: 
     307            q_entry.attrs['resolutions'] = 'dQ' 
     308            dq_entry = data_entry.create_dataset('dQ', data=data_obj.dx) 
     309            dq_entry.attrs['units'] = data_obj.x_unit 
     310        elif data_obj.dxl is not None: 
     311            q_entry.attrs['resolutions'] = ['dQl','dQw'] 
     312            dql_entry = data_entry.create_dataset('dQl', data=data_obj.dxl) 
     313            dql_entry.attrs['units'] = data_obj.x_unit 
     314            dqw_entry = data_entry.create_dataset('dQw', data=data_obj.dxw) 
     315            dqw_entry.attrs['units'] = data_obj.x_unit 
    266316 
    267317    def _write_2d_data(self, data, data_entry): 
     
    273323        """ 
    274324        data_entry.attrs['signal'] = 'I' 
    275         data_entry.attrs['I_axes'] = 'Q,Q' 
    276         data_entry.attrs['I_uncertainties'] = 'Idev' 
    277         data_entry.attrs['Q_indicies'] = [0,1] 
     325        data_entry.attrs['I_axes'] = 'Qx,Qy' 
     326        data_entry.attrs['Q_indices'] = [0,1] 
    278327 
    279328        (n_rows, n_cols) = (len(data.y_bins), len(data.x_bins)) 
    280329 
    281         if n_rows == 0 and n_cols == 0: 
     330        if (n_rows == 0 and n_cols == 0) or (n_cols*n_rows != data.data.size): 
    282331            # Calculate rows and columns, assuming detector is square 
    283332            # Same logic as used in PlotPanel.py _get_bins 
     
    288337                raise ValueError("Unable to calculate dimensions of 2D data") 
    289338 
    290         I = np.reshape(data.data, (n_rows, n_cols)) 
    291         dI = np.zeros((n_rows, n_cols)) 
    292         if not all(data.err_data == [None]): 
    293             dI = np.reshape(data.err_data, (n_rows, n_cols)) 
    294         qx =  np.reshape(data.qx_data, (n_rows, n_cols)) 
     339        intensity = np.reshape(data.data, (n_rows, n_cols)) 
     340        qx = np.reshape(data.qx_data, (n_rows, n_cols)) 
    295341        qy = np.reshape(data.qy_data, (n_rows, n_cols)) 
    296342 
    297         I_entry = data_entry.create_dataset('I', data=I) 
    298         I_entry.attrs['units'] = data.I_unit 
    299         Qx_entry = data_entry.create_dataset('Qx', data=qx) 
    300         Qx_entry.attrs['units'] = data.Q_unit 
    301         Qy_entry = data_entry.create_dataset('Qy', data=qy) 
    302         Qy_entry.attrs['units'] = data.Q_unit 
    303         Idev_entry = data_entry.create_dataset('Idev', data=dI) 
    304         Idev_entry.attrs['units'] = data.I_unit 
     343        i_entry = data_entry.create_dataset('I', data=intensity) 
     344        i_entry.attrs['units'] = data.I_unit 
     345        qx_entry = data_entry.create_dataset('Qx', data=qx) 
     346        qx_entry.attrs['units'] = data.Q_unit 
     347        qy_entry = data_entry.create_dataset('Qy', data=qy) 
     348        qy_entry.attrs['units'] = data.Q_unit 
     349        if (data.err_data is not None 
     350                and not all(v is None for v in data.err_data)): 
     351            d_i = np.reshape(data.err_data, (n_rows, n_cols)) 
     352            i_entry.attrs['uncertainties'] = 'Idev' 
     353            i_dev_entry = data_entry.create_dataset('Idev', data=d_i) 
     354            i_dev_entry.attrs['units'] = data.I_unit 
     355        if (data.dqx_data is not None 
     356                and not all(v is None for v in data.dqx_data)): 
     357            qx_entry.attrs['resolutions'] = 'dQx' 
     358            dqx_entry = data_entry.create_dataset('dQx', data=data.dqx_data) 
     359            dqx_entry.attrs['units'] = data.Q_unit 
     360        if (data.dqy_data is not None 
     361                and not all(v is None for v in data.dqy_data)): 
     362            qy_entry.attrs['resolutions'] = 'dQy' 
     363            dqy_entry = data_entry.create_dataset('dQy', data=data.dqy_data) 
     364            dqy_entry.attrs['units'] = data.Q_unit 
     365        if data.mask is not None and not all(v is None for v in data.mask): 
     366            data_entry.attrs['mask'] = "mask" 
     367            mask = np.invert(np.asarray(data.mask, dtype=bool)) 
     368            data_entry.create_dataset('mask', data=mask) 
Note: See TracChangeset for help on using the changeset viewer.