source: sasview/src/sas/sascalc/dataloader/readers/red2d_reader.py @ b9d74f3

Last change on this file since b9d74f3 was b9d74f3, checked in by andyfaff, 8 years ago

MAINT: use raise Exception() not raise Exception

  • Property mode set to 100644
File size: 12.6 KB
Line 
1"""
2    TXT/IGOR 2D Q Map file reader
3"""
4#####################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#See the license text in license.txt
9#copyright 2008, University of Tennessee
10######################################################################
11import os
12import numpy as np
13import math
14from sas.sascalc.dataloader.data_info import Data2D, Detector
15
16# Look for unit converter
17has_converter = True
18try:
19    from sas.sascalc.data_util.nxsunit import Converter
20except:
21    has_converter = False
22   
23   
24def check_point(x_point):
25    """
26    check point validity
27    """
28    # set zero for non_floats
29    try:
30        return float(x_point)
31    except:
32        return 0
33     
34       
35class Reader:
36    """ Simple data reader for Igor data files """
37    ## File type
38    type_name = "IGOR/DAT 2D Q_map"
39    ## Wildcards
40    type = ["IGOR/DAT 2D file in Q_map (*.dat)|*.DAT"]
41    ## Extension
42    ext = ['.DAT', '.dat']
43   
44    def write(self, filename, data):
45        """
46        Write to .dat
47       
48        :param filename: file name to write
49        :param data: data2D
50        """
51        import time
52        # Write the file
53        fd = open(filename, 'w')
54        t = time.localtime()
55        time_str = time.strftime("%H:%M on %b %d %y", t)
56       
57        header_str = "Data columns are Qx - Qy - I(Qx,Qy)\n\nASCII data"
58        header_str += " created at %s \n\n" % time_str
59        # simple 2D header
60        fd.write(header_str)
61        # write qx qy I values
62        for i in range(len(data.data)):
63            fd.write("%g  %g  %g\n" % (data.qx_data[i],
64                                        data.qy_data[i],
65                                       data.data[i]))
66        # close
67        fd.close()
68
69    def read(self, filename=None):
70        """ Read file """
71        if not os.path.isfile(filename):
72            raise ValueError("Specified file %s is not a regular file" % filename)
73
74        # Read file
75        f = open(filename, 'r')
76        buf = f.read()
77        f.close()
78        # Instantiate data object
79        output = Data2D()
80        output.filename = os.path.basename(filename)
81        detector = Detector()
82        if len(output.detector) > 0:
83            print str(output.detector[0])
84        output.detector.append(detector)
85               
86        # Get content
87        dataStarted = False
88       
89        ## Defaults
90        lines = buf.split('\n')
91        x = []
92        y = []
93       
94        wavelength = None
95        distance = None
96        transmission = None
97       
98        pixel_x = None
99        pixel_y = None
100       
101        isInfo = False
102        isCenter = False
103
104        data_conv_q = None
105        data_conv_i = None
106       
107        # Set units: This is the unit assumed for Q and I in the data file.
108        if has_converter == True and output.Q_unit != '1/A':
109            data_conv_q = Converter('1/A')
110            # Test it
111            data_conv_q(1.0, output.Q_unit)
112           
113        if has_converter == True and output.I_unit != '1/cm':
114            data_conv_i = Converter('1/cm')
115            # Test it
116            data_conv_i(1.0, output.I_unit)
117       
118             
119        # Remove the last lines before the for loop if the lines are empty
120        # to calculate the exact number of data points
121        count = 0
122        while (len(lines[len(lines) - (count + 1)].lstrip().rstrip()) < 1):
123            del lines[len(lines) - (count + 1)]
124            count = count + 1
125
126        #Read Header and find the dimensions of 2D data
127        line_num = 0
128        # Old version NIST files: 0
129        ver = 0
130        for line in lines:
131            line_num += 1
132            ## Reading the header applies only to IGOR/NIST 2D q_map data files
133            # Find setup info line
134            if isInfo:
135                isInfo = False
136                line_toks = line.split()
137                # Wavelength in Angstrom
138                try:
139                    wavelength = float(line_toks[1])
140                    # Units
141                    if has_converter == True and \
142                    output.source.wavelength_unit != 'A':
143                        conv = Converter('A')
144                        wavelength = conv(wavelength,
145                                          units=output.source.wavelength_unit)
146                except:
147                    #Not required
148                    pass
149                # Distance in mm
150                try:
151                    distance = float(line_toks[3])
152                    # Units
153                    if has_converter == True and detector.distance_unit != 'm':
154                        conv = Converter('m')
155                        distance = conv(distance, units=detector.distance_unit)
156                except:
157                    #Not required
158                    pass
159               
160                # Distance in meters
161                try:
162                    transmission = float(line_toks[4])
163                except:
164                    #Not required
165                    pass
166                                           
167            if line.count("LAMBDA") > 0:
168                isInfo = True
169               
170            # Find center info line
171            if isCenter:
172                isCenter = False
173                line_toks = line.split()
174                # Center in bin number
175                center_x = float(line_toks[0])
176                center_y = float(line_toks[1])
177
178            if line.count("BCENT") > 0:
179                isCenter = True
180            # Check version
181            if line.count("Data columns") > 0:
182                if line.count("err(I)") > 0:
183                    ver = 1
184            # Find data start
185            if line.count("ASCII data") > 0:
186                dataStarted = True
187                continue
188
189            ## Read and get data.
190            if dataStarted == True:
191                line_toks = line.split()
192                if len(line_toks) == 0:
193                    #empty line
194                    continue
195                # the number of columns must be stayed same
196                col_num = len(line_toks)
197                break
198        # Make numpy array to remove header lines using index
199        lines_array = np.array(lines)
200
201        # index for lines_array
202        lines_index = np.arange(len(lines))
203       
204        # get the data lines
205        data_lines = lines_array[lines_index >= (line_num - 1)]
206        # Now we get the total number of rows (i.e., # of data points)
207        row_num = len(data_lines)
208        # make it as list again to control the separators
209        data_list = " ".join(data_lines.tolist())
210        # split all data to one big list w/" "separator
211        data_list = data_list.split()
212 
213        # Check if the size is consistent with data, otherwise
214        #try the tab(\t) separator
215        # (this may be removed once get the confidence
216        #the former working all cases).
217        if len(data_list) != (len(data_lines)) * col_num:
218            data_list = "\t".join(data_lines.tolist())
219            data_list = data_list.split()
220
221        # Change it(string) into float
222        #data_list = map(float,data_list)
223        data_list1 = map(check_point, data_list)
224
225        # numpy array form
226        data_array = np.array(data_list1)
227        # Redimesion based on the row_num and col_num,
228        #otherwise raise an error.
229        try:
230            data_point = data_array.reshape(row_num, col_num).transpose()
231        except:
232            msg = "red2d_reader: Can't read this file: Not a proper file format"
233            raise ValueError(msg)
234        ## Get the all data: Let's HARDcoding; Todo find better way
235        # Defaults
236        dqx_data = np.zeros(0)
237        dqy_data = np.zeros(0)
238        err_data = np.ones(row_num)
239        qz_data = np.zeros(row_num)
240        mask = np.ones(row_num, dtype=bool)
241        # Get from the array
242        qx_data = data_point[0]
243        qy_data = data_point[1]
244        data = data_point[2]
245        if ver == 1:
246            if col_num > (2 + ver):
247                err_data = data_point[(2 + ver)]
248        if col_num > (3 + ver):
249            qz_data = data_point[(3 + ver)]
250        if col_num > (4 + ver):
251            dqx_data = data_point[(4 + ver)]
252        if col_num > (5 + ver):
253            dqy_data = data_point[(5 + ver)]
254        #if col_num > (6 + ver): mask[data_point[(6 + ver)] < 1] = False
255        q_data = np.sqrt(qx_data*qx_data+qy_data*qy_data+qz_data*qz_data)
256           
257        # Extra protection(it is needed for some data files):
258        # If all mask elements are False, put all True
259        if not mask.any():
260            mask[mask == False] = True
261 
262        # Store limits of the image in q space
263        xmin = np.min(qx_data)
264        xmax = np.max(qx_data)
265        ymin = np.min(qy_data)
266        ymax = np.max(qy_data)
267
268        # units
269        if has_converter == True and output.Q_unit != '1/A':
270            xmin = data_conv_q(xmin, units=output.Q_unit)
271            xmax = data_conv_q(xmax, units=output.Q_unit)
272            ymin = data_conv_q(ymin, units=output.Q_unit)
273            ymax = data_conv_q(ymax, units=output.Q_unit)
274           
275        ## calculate the range of the qx and qy_data
276        x_size = math.fabs(xmax - xmin)
277        y_size = math.fabs(ymax - ymin)
278       
279        # calculate the number of pixels in the each axes
280        npix_y = math.floor(math.sqrt(len(data)))
281        npix_x = math.floor(len(data) / npix_y)
282       
283        # calculate the size of bins
284        xstep = x_size / (npix_x - 1)
285        ystep = y_size / (npix_y - 1)
286       
287        # store x and y axis bin centers in q space
288        x_bins = np.arange(xmin, xmax + xstep, xstep)
289        y_bins = np.arange(ymin, ymax + ystep, ystep)
290       
291        # get the limits of q values
292        xmin = xmin - xstep / 2
293        xmax = xmax + xstep / 2
294        ymin = ymin - ystep / 2
295        ymax = ymax + ystep / 2
296       
297        #Store data in outputs
298        #TODO: Check the lengths
299        output.data = data
300        if (err_data == 1).all():
301            output.err_data = np.sqrt(np.abs(data))
302            output.err_data[output.err_data == 0.0] = 1.0
303        else:
304            output.err_data = err_data
305           
306        output.qx_data = qx_data
307        output.qy_data = qy_data
308        output.q_data = q_data
309        output.mask = mask
310       
311        output.x_bins = x_bins
312        output.y_bins = y_bins
313               
314        output.xmin = xmin
315        output.xmax = xmax
316        output.ymin = ymin
317        output.ymax = ymax
318       
319        output.source.wavelength = wavelength
320       
321        # Store pixel size in mm
322        detector.pixel_size.x = pixel_x
323        detector.pixel_size.y = pixel_y
324       
325        # Store the sample to detector distance
326        detector.distance = distance
327       
328        # optional data: if all of dq data == 0, do not pass to output
329        if len(dqx_data) == len(qx_data) and dqx_data.any() != 0:
330            # if no dqx_data, do not pass dqy_data.
331            #(1 axis dq is not supported yet).
332            if len(dqy_data) == len(qy_data) and dqy_data.any() != 0:
333                # Currently we do not support dq parr, perp.
334                # tranfer the comp. to cartesian coord. for newer version.
335                if ver != 1:
336                    diag = np.sqrt(qx_data * qx_data + qy_data * qy_data)
337                    cos_th = qx_data / diag
338                    sin_th = qy_data / diag
339                    output.dqx_data = np.sqrt((dqx_data * cos_th) * \
340                                                 (dqx_data * cos_th) \
341                                                 + (dqy_data * sin_th) * \
342                                                  (dqy_data * sin_th))
343                    output.dqy_data = np.sqrt((dqx_data * sin_th) * \
344                                                 (dqx_data * sin_th) \
345                                                 + (dqy_data * cos_th) * \
346                                                  (dqy_data * cos_th))
347                else:
348                    output.dqx_data = dqx_data
349                    output.dqy_data = dqy_data
350
351        # Units of axes
352        if data_conv_q is not None:
353            output.xaxis("\\rm{Q_{x}}", output.Q_unit)
354            output.yaxis("\\rm{Q_{y}}", output.Q_unit)
355        else:
356            output.xaxis("\\rm{Q_{x}}", 'A^{-1}')
357            output.yaxis("\\rm{Q_{y}}", 'A^{-1}')
358        if data_conv_i is not None:
359            output.zaxis("\\rm{Intensity}", output.I_unit)
360        else:
361            output.zaxis("\\rm{Intensity}", "cm^{-1}")
362   
363        # Store loading process information
364        output.meta_data['loader'] = self.type_name
365
366        return output
Note: See TracBrowser for help on using the repository browser.