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

Last change on this file since 7677b4d was b699768, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 9 years ago

Initial commit of the refactored SasCalc? module.

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