source: sasview/src/sas/sascalc/file_converter/nxcansas_writer.py @ a14fa99

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since a14fa99 was a14fa99, checked in by lewis, 8 years ago

Ensure Idev is set correctly on 1D data when writing to NXcanSAS

  • Property mode set to 100644
File size: 8.1 KB
Line 
1"""
2    NXcanSAS 1/2D data reader for writing HDF5 formatted NXcanSAS files.
3"""
4
5import h5py
6import numpy as np
7import re
8import os
9
10from sas.sascalc.dataloader.readers.cansas_reader_HDF5 import Reader as Cansas2Reader
11from sas.sascalc.dataloader.data_info import Data1D, Data2D
12
13class NXcanSASWriter(Cansas2Reader):
14    """
15    A class for writing in NXcanSAS data files. Any number of data sets may be
16    written to the file. Currently 1D and 2D SAS data sets are supported
17
18    NXcanSAS spec: http://download.nexusformat.org/sphinx/classes/contributed_definitions/NXcanSAS.html
19
20    :Dependencies:
21        The NXcanSAS writer requires h5py => v2.5.0 or later.
22    """
23
24    def write(self, dataset, filename):
25        """
26        Write an array of Data1d or Data2D objects to an NXcanSAS file, as
27        one SASEntry with multiple SASData elements. The metadata of the first
28        elememt in the array will be written as the SASentry metadata
29        (detector, instrument, sample, etc).
30
31        :param dataset: A list of Data1D or Data2D objects to write
32        :param filename: Where to write the NXcanSAS file
33        """
34
35        def _h5_string(string):
36            """
37            Convert a string to a numpy string in a numpy array. This way it is
38            written to the HDF5 file as a fixed length ASCII string and is
39            compatible with the Reader read() method.
40            """
41            if not isinstance(string, str):
42                string = str(string)
43
44            return np.array([np.string_(string)])
45
46        def _h5_float(x):
47            if not (isinstance(x, list)):
48                x = [x]
49            return np.array(x, dtype=np.float32)
50
51        valid_data = all([issubclass(d.__class__, (Data1D, Data2D)) for d in dataset])
52        if not valid_data:
53            raise ValueError("All entries of dataset must be Data1D or Data2D objects")
54
55        # Get run name and number from first Data object
56        data_info = dataset[0]
57        run_number = ''
58        run_name = ''
59        if len(data_info.run) > 0:
60            run_number = data_info.run[0]
61            if len(data_info.run_name) > 0:
62                run_name = data_info.run_name[run_number]
63
64        f = h5py.File(filename, 'w')
65        sasentry = f.create_group('sasentry01')
66        sasentry['definition'] = _h5_string('NXcanSAS')
67        sasentry['run'] = _h5_string(run_number)
68        sasentry['run'].attrs['name'] = run_name
69        sasentry['title'] = _h5_string(data_info.title)
70        sasentry.attrs['canSAS_class'] = 'SASentry'
71        sasentry.attrs['version'] = '1.0'
72
73        i = 1
74
75        for data_obj in dataset:
76            data_entry = sasentry.create_group("sasdata{0:0=2d}".format(i))
77            data_entry.attrs['canSAS_class'] = 'SASdata'
78            if isinstance(data_obj, Data1D):
79                self._write_1d_data(data_obj, data_entry)
80            elif isinstance(data_obj, Data2D):
81                self._write_2d_data(data_obj, data_entry)
82            i += 1
83
84        data_info = dataset[0]
85        sample_entry = sasentry.create_group('sassample')
86        sample_entry.attrs['canSAS_class'] = 'SASsample'
87        sample_entry['name'] = _h5_string(data_info.sample.name)
88        sample_attrs = ['thickness', 'temperature']
89        for key in sample_attrs:
90            if getattr(data_info.sample, key) is not None:
91                sample_entry.create_dataset(key,
92                    data=_h5_float(getattr(data_info.sample, key)))
93
94        instrument_entry = sasentry.create_group('sasinstrument')
95        instrument_entry.attrs['canSAS_class'] = 'SASinstrument'
96        instrument_entry['name'] = _h5_string(data_info.instrument)
97
98        source_entry = instrument_entry.create_group('sassource')
99        source_entry.attrs['canSAS_class'] = 'SASsource'
100        if data_info.source.radiation is None:
101            source_entry['radiation'] = _h5_string('neutron')
102        else:
103            source_entry['radiation'] = _h5_string(data_info.source.radiation)
104
105        if len(data_info.collimation) > 0:
106            i = 1
107            for coll_info in data_info.collimation:
108                collimation_entry = instrument_entry.create_group(
109                    'sascollimation{0:0=2d}'.format(i))
110                collimation_entry.attrs['canSAS_class'] = 'SAScollimation'
111                if coll_info.length is not None:
112                    collimation_entry['SDD'] = _h5_float(coll_info.length)
113                    collimation_entry['SDD'].attrs['units'] = coll_info.length_unit
114                if coll_info.name is not None:
115                    collimation_entry['name'] = _h5_string(coll_info.name)
116        else:
117            collimation_entry = instrument_entry.create_group('sascollimation01')
118
119        if len(data_info.detector) > 0:
120            i = 1
121            for det_info in data_info.detector:
122                detector_entry = instrument_entry.create_group(
123                    'sasdetector{0:0=2d}'.format(i))
124                detector_entry.attrs['canSAS_class'] = 'SASdetector'
125                if det_info.distance is not None:
126                    detector_entry['SDD'] = _h5_float(det_info.distance)
127                    detector_entry['SDD'].attrs['units'] = det_info.distance_unit
128                if det_info.name is not None:
129                    detector_entry['name'] = _h5_string(det_info.name)
130                else:
131                    detector_entry['name'] = _h5_string('')
132                i += 1
133        else:
134            detector_entry = instrument_entry.create_group('sasdetector01')
135            detector_entry.attrs['canSAS_class'] = 'SASdetector'
136            detector_entry.attrs['name'] = ''
137
138        # TODO: implement writing SASnote
139        i = 1
140        note_entry = sasentry.create_group('sasnote{0:0=2d}'.format(i))
141        note_entry.attrs['canSAS_class'] = 'SASnote'
142
143        f.close()
144
145    def _write_1d_data(self, data_obj, data_entry):
146        """
147        Writes the contents of a Data1D object to a SASdata h5py Group
148
149        :param data_obj: A Data1D object to write to the file
150        :param data_entry: A h5py Group object representing the SASdata
151        """
152        data_entry.attrs['signal'] = 'I'
153        data_entry.attrs['I_axes'] = 'Q'
154        data_entry.attrs['I_uncertainties'] = 'Idev'
155        data_entry.attrs['Q_indicies'] = 0
156
157        dI = data_obj.dy
158        if dI is None:
159            dI = np.zeros((data_obj.y.shape))
160
161        data_entry.create_dataset('Q', data=data_obj.x)
162        data_entry.create_dataset('I', data=data_obj.y)
163        data_entry.create_dataset('Idev', data=dI)
164
165    def _write_2d_data(self, data, data_entry):
166        """
167        Writes the contents of a Data2D object to a SASdata h5py Group
168
169        :param data: A Data2D object to write to the file
170        :param data_entry: A h5py Group object representing the SASdata
171        """
172        data_entry.attrs['signal'] = 'I'
173        data_entry.attrs['I_axes'] = 'Q,Q'
174        data_entry.attrs['I_uncertainties'] = 'Idev'
175        data_entry.attrs['Q_indicies'] = [0,1]
176
177        (n_rows, n_cols) = (len(data.y_bins), len(data.x_bins))
178
179        if n_rows == 0 and n_cols == 0:
180            # Calculate rows and columns, assuming detector is square
181            # Same logic as used in PlotPanel.py _get_bins
182            n_cols = int(np.floor(np.sqrt(len(data.qy_data))))
183            n_rows = int(np.floor(len(data.qy_data) / n_cols))
184
185            if n_rows * n_cols != len(data.qy_data):
186                raise ValueError("Unable to calculate dimensions of 2D data")
187
188        I = np.reshape(data.data, (n_rows, n_cols))
189        dI = np.zeros((n_rows, n_cols))
190        if not all(data.err_data == [None]):
191            dI = np.reshape(data.err_data, (n_rows, n_cols))
192        qx =  np.reshape(data.qx_data, (n_rows, n_cols))
193        qy = np.reshape(data.qy_data, (n_rows, n_cols))
194
195        I_entry = data_entry.create_dataset('I', data=I)
196        I_entry.attrs['units'] = data.I_unit
197        Qx_entry = data_entry.create_dataset('Qx', data=qx)
198        Qx_entry.attrs['units'] = data.Q_unit
199        Qy_entry = data_entry.create_dataset('Qy', data=qy)
200        Qy_entry.attrs['units'] = data.Q_unit
201        Idev_entry = data_entry.create_dataset('Idev', data=dI)
202        Idev_entry.attrs['units'] = data.I_unit
Note: See TracBrowser for help on using the repository browser.