source: sasview/src/sas/sascalc/dataloader/readers/ascii_reader.py @ d0ccd80f

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since d0ccd80f was d0ccd80f, checked in by Piotr Rozyczko <rozyczko@…>, 4 years ago

Fixes #658: ASCII reader files less than 5 lines long get thrown out.

  • Property mode set to 100644
File size: 9.9 KB
Line 
1"""
2    ASCII 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#If you use DANSE applications to do scientific research that leads to
9#publication, we ask that you acknowledge the use of the software with the
10#following sentence:
11#This work benefited from DANSE software developed under NSF award DMR-0520547.
12#copyright 2008, University of Tennessee
13#############################################################################
14
15
16import numpy
17import os
18from sas.sascalc.dataloader.data_info import Data1D
19
20# Check whether we have a converter available
21has_converter = True
22try:
23    from sas.sascalc.data_util.nxsunit import Converter
24except:
25    has_converter = False
26_ZERO = 1e-16
27
28
29class Reader:
30    """
31    Class to load ascii files (2, 3 or 4 columns).
32    """
33    ## File type
34    type_name = "ASCII"
35
36    ## Wildcards
37    type = ["ASCII files (*.txt)|*.txt",
38            "ASCII files (*.dat)|*.dat",
39            "ASCII files (*.abs)|*.abs",
40            "CSV files (*.csv)|*.csv"]
41    ## List of allowed extensions
42    ext = ['.txt', '.TXT', '.dat', '.DAT', '.abs', '.ABS', 'csv', 'CSV']
43
44    ## Flag to bypass extension check
45    allow_all = True
46
47    def read(self, path):
48        """
49        Load data file
50
51        :param path: file path
52        :return: Data1D object, or None
53
54        :raise RuntimeError: when the file can't be opened
55        :raise ValueError: when the length of the data vectors are inconsistent
56        """
57        if os.path.isfile(path):
58            basename = os.path.basename(path)
59            _, extension = os.path.splitext(basename)
60            if self.allow_all or extension.lower() in self.ext:
61                try:
62                    # Read in binary mode since GRASP frequently has no-ascii
63                    # characters that breaks the open operation
64                    input_f = open(path,'rb')
65                except:
66                    raise  RuntimeError, "ascii_reader: cannot open %s" % path
67                buff = input_f.read()
68                lines = buff.splitlines()
69
70                # Arrays for data storage
71                tx = numpy.zeros(0)
72                ty = numpy.zeros(0)
73                tdy = numpy.zeros(0)
74                tdx = numpy.zeros(0)
75
76                # The first good line of data will define whether
77                # we have 2-column or 3-column ascii
78                has_error_dx = None
79                has_error_dy = None
80
81                #Initialize counters for data lines and header lines.
82                is_data = False
83                # More than "5" lines of data is considered as actual
84                # data unless that is the only data
85                min_data_pts = 5
86                # To count # of current data candidate lines
87                candidate_lines = 0
88                # To count total # of previous data candidate lines
89                candidate_lines_previous = 0
90                #minimum required number of columns of data
91                lentoks = 2
92                for line in lines:
93                    toks = self.splitline(line)
94                    # To remember the # of columns in the current line of data
95                    new_lentoks = len(toks)
96                    try:
97                        if new_lentoks == 1 and not is_data:
98                            ## If only one item in list, no longer data
99                            raise ValueError
100                        elif new_lentoks == 0:
101                            ## If the line is blank, skip and continue on
102                            ## In case of breaks within data sets.
103                            continue
104                        elif new_lentoks != lentoks and is_data:
105                            ## If a footer is found, break the loop and save the data
106                            break
107                        elif new_lentoks != lentoks and not is_data:
108                            ## If header lines are numerical
109                            candidate_lines = 0
110                            candidate_lines_previous = 0
111
112                        #Make sure that all columns are numbers.
113                        for colnum in range(len(toks)):
114                            # Any non-floating point values throw ValueError
115                            float(toks[colnum])
116
117                        candidate_lines += 1
118                        _x = float(toks[0])
119                        _y = float(toks[1])
120                        _dx = None
121                        _dy = None
122
123                        #If 5 or more lines, this is considering the set data
124                        if candidate_lines >= min_data_pts:
125                            is_data = True
126
127                        # If a 3rd row is present, consider it dy
128                        if new_lentoks > 2:
129                            _dy = float(toks[2])
130                        has_error_dy = False if _dy == None else True
131
132                        # If a 4th row is present, consider it dx
133                        if new_lentoks > 3:
134                            _dx = float(toks[3])
135                        has_error_dx = False if _dx == None else True
136
137                        # Delete the previously stored lines of data candidates if
138                        # the list is not data
139                        if candidate_lines == 1 and -1 < candidate_lines_previous < min_data_pts and \
140                            is_data == False:
141                            try:
142                                tx = numpy.zeros(0)
143                                ty = numpy.zeros(0)
144                                tdy = numpy.zeros(0)
145                                tdx = numpy.zeros(0)
146                            except:
147                                pass
148
149                        if has_error_dy == True:
150                            tdy = numpy.append(tdy, _dy)
151                        if has_error_dx == True:
152                            tdx = numpy.append(tdx, _dx)
153                        tx = numpy.append(tx, _x)
154                        ty = numpy.append(ty, _y)
155
156                        #To remember the # of columns on the current line
157                        # for the next line of data
158                        lentoks = new_lentoks
159                        candidate_lines_previous = candidate_lines
160                    except ValueError:
161                        # It is data and meet non - number, then stop reading
162                        if is_data == True:
163                            break
164                        lentoks = 2
165                        has_error_dx = None
166                        has_error_dy = None
167                        #Reset # of lines of data candidates
168                        candidate_lines = 0
169                    except:
170                        pass
171
172                input_f.close()
173                if not is_data:
174                    return None
175                # Sanity check
176                if has_error_dy == True and not len(ty) == len(tdy):
177                    msg = "ascii_reader: y and dy have different length"
178                    raise RuntimeError, msg
179                if has_error_dx == True and not len(tx) == len(tdx):
180                    msg = "ascii_reader: y and dy have different length"
181                    raise RuntimeError, msg
182                # If the data length is zero, consider this as
183                # though we were not able to read the file.
184                if len(tx) == 0:
185                    raise RuntimeError, "ascii_reader: could not load file"
186
187                #Let's re-order the data to make cal.
188                # curve look better some cases
189                ind = numpy.lexsort((ty, tx))
190                x = numpy.zeros(len(tx))
191                y = numpy.zeros(len(ty))
192                dy = numpy.zeros(len(tdy))
193                dx = numpy.zeros(len(tdx))
194                output = Data1D(x, y, dy=dy, dx=dx)
195                self.filename = output.filename = basename
196
197                for i in ind:
198                    x[i] = tx[ind[i]]
199                    y[i] = ty[ind[i]]
200                    if has_error_dy == True:
201                        dy[i] = tdy[ind[i]]
202                    if has_error_dx == True:
203                        dx[i] = tdx[ind[i]]
204                # Zeros in dx, dy
205                if has_error_dx:
206                    dx[dx == 0] = _ZERO
207                if has_error_dy:
208                    dy[dy == 0] = _ZERO
209                #Data
210                output.x = x[x != 0]
211                output.y = y[x != 0]
212                output.dy = dy[x != 0] if has_error_dy == True\
213                    else numpy.zeros(len(output.y))
214                output.dx = dx[x != 0] if has_error_dx == True\
215                    else numpy.zeros(len(output.x))
216
217                output.xaxis("\\rm{Q}", 'A^{-1}')
218                output.yaxis("\\rm{Intensity}", "cm^{-1}")
219
220                # Store loading process information
221                output.meta_data['loader'] = self.type_name
222                if len(output.x) < 1:
223                    raise RuntimeError, "%s is empty" % path
224                return output
225
226        else:
227            raise RuntimeError, "%s is not a file" % path
228        return None
229
230    def splitline(self, line):
231        """
232        Splits a line into pieces based on common delimeters
233        :param line: A single line of text
234        :return: list of values
235        """
236        # Initial try for CSV (split on ,)
237        toks = line.split(',')
238        # Now try SCSV (split on ;)
239        if len(toks) < 2:
240            toks = line.split(';')
241        # Now go for whitespace
242        if len(toks) < 2:
243            toks = line.split()
244        return toks
Note: See TracBrowser for help on using the repository browser.