1 | """ |
---|
2 | ASCII 2D Loader |
---|
3 | """ |
---|
4 | from sas.sascalc.dataloader.data_info import Data2D |
---|
5 | from sas.sascalc.file_converter.nxcansas_writer import NXcanSASWriter |
---|
6 | import numpy as np |
---|
7 | |
---|
8 | # ISIS 2D ASCII File Format |
---|
9 | # http://www.isis.stfc.ac.uk/instruments/loq/software/colette-ascii-file-format-descriptions9808.pdf |
---|
10 | # line: property |
---|
11 | # 0: File header |
---|
12 | # 1: q_x axis label and units |
---|
13 | # 2: q_y axis label and units |
---|
14 | # 3: Intensity axis label and units |
---|
15 | # 4: nUseRec - number of lines of user content following this line |
---|
16 | # 5 - 5+nUseRec: user content |
---|
17 | # Number of qx points |
---|
18 | # List of qx points |
---|
19 | # Number of qy points |
---|
20 | # List of qy points |
---|
21 | # numqx numqy scale |
---|
22 | |
---|
23 | class ASCII2DLoader(object): |
---|
24 | |
---|
25 | def __init__(self, data_path): |
---|
26 | """ |
---|
27 | :param data_path: The path to the file to load |
---|
28 | """ |
---|
29 | self.data_path = data_path |
---|
30 | |
---|
31 | def load(self): |
---|
32 | """ |
---|
33 | Load the data from the file into a Data2D object |
---|
34 | |
---|
35 | :return: A Data2D instance containing data from the file |
---|
36 | :raises ValueError: Raises a ValueError if the file is incorrectly formatted |
---|
37 | """ |
---|
38 | file_handle = open(self.data_path, 'r') |
---|
39 | file_buffer = file_handle.read() |
---|
40 | all_lines = file_buffer.splitlines() |
---|
41 | |
---|
42 | # Load num_points line-by-line from lines into a numpy array, starting |
---|
43 | # on line number start_line |
---|
44 | def _load_points(lines, start_line, num_points): |
---|
45 | qs = np.zeros(num_points) |
---|
46 | n = start_line |
---|
47 | filled = 0 |
---|
48 | while filled < num_points: |
---|
49 | row = np.fromstring(lines[n], dtype=np.float32, sep=' ') |
---|
50 | qs[filled:filled+len(row)] = row |
---|
51 | filled += len(row) |
---|
52 | n += 1 |
---|
53 | return n, qs |
---|
54 | |
---|
55 | current_line = 4 |
---|
56 | try: |
---|
57 | # Skip nUseRec lines |
---|
58 | nUseRec = int(all_lines[current_line].strip()[0]) |
---|
59 | current_line += nUseRec + 1 |
---|
60 | # Read qx data |
---|
61 | num_qs = int(all_lines[current_line].strip()) |
---|
62 | current_line += 1 |
---|
63 | current_line, qx = _load_points(all_lines, current_line, num_qs) |
---|
64 | |
---|
65 | # Read qy data |
---|
66 | num_qs = int(all_lines[current_line].strip()) |
---|
67 | current_line += 1 |
---|
68 | current_line, qy = _load_points(all_lines, current_line, num_qs) |
---|
69 | except ValueError as e: |
---|
70 | err_msg = "File incorrectly formatted.\n" |
---|
71 | if str(e).find('broadcast') != -1: |
---|
72 | err_msg += "Incorrect number of q data points provided. " |
---|
73 | err_msg += "Expected {}.".format(num_qs) |
---|
74 | elif str(e).find('invalid literal') != -1: |
---|
75 | err_msg += "Expected integer on line {}. Instead got '{}'".format(current_line + 1, |
---|
76 | all_lines[current_line]) |
---|
77 | else: |
---|
78 | err_msg += str(e) |
---|
79 | raise ValueError(err_msg) |
---|
80 | |
---|
81 | # dimensions: [width, height, scale] |
---|
82 | try: |
---|
83 | dimensions = np.fromstring(all_lines[current_line], dtype=np.float32, sep=' ') |
---|
84 | if len(dimensions) != 3: raise ValueError() |
---|
85 | width = int(dimensions[0]) |
---|
86 | height = int(dimensions[1]) |
---|
87 | except ValueError as e: |
---|
88 | err_msg = "File incorrectly formatted.\n" |
---|
89 | err_msg += "Expected line {} to be of the form: <num_qx> <num_qy> <scale>.".format(current_line + 1) |
---|
90 | err_msg += " Instead got '{}'.".format(all_lines[current_line]) |
---|
91 | raise ValueError(err_msg) |
---|
92 | |
---|
93 | if width > len(qx) or height > len(qy): |
---|
94 | err_msg = "File incorrectly formatted.\n" |
---|
95 | err_msg += ("Line {} says to use {}x{} points. " |
---|
96 | "Only {}x{} provided.").format(current_line + 1, width, height, |
---|
97 | len(qx), len(qy)) |
---|
98 | raise ValueError(err_msg) |
---|
99 | |
---|
100 | # More qx and/or qy points can be provided than are actually used |
---|
101 | qx = qx[:width] |
---|
102 | qy = qy[:height] |
---|
103 | |
---|
104 | current_line += 1 |
---|
105 | # iflag = 1 => Only intensity data (not dealt with here) |
---|
106 | # iflag = 2 => q axis and intensity data |
---|
107 | # iflag = 3 => q axis, intensity and error data |
---|
108 | try: |
---|
109 | iflag = int(all_lines[current_line].strip()[0]) |
---|
110 | if iflag <= 0 or iflag > 3: raise ValueError() |
---|
111 | except: |
---|
112 | err_msg = "File incorrectly formatted.\n" |
---|
113 | iflag = all_lines[current_line].strip()[0] |
---|
114 | err_msg += "Expected iflag on line {} to be 1, 2 or 3. Instead got '{}'.".format(current_line+1, iflag) |
---|
115 | raise ValueError(err_msg) |
---|
116 | |
---|
117 | current_line += 1 |
---|
118 | |
---|
119 | try: |
---|
120 | current_line, I = _load_points(all_lines, current_line, width*height) |
---|
121 | dI = np.zeros(width*height) |
---|
122 | |
---|
123 | # Load error data if it's provided |
---|
124 | if iflag == 3: |
---|
125 | _, dI = _load_points(all_lines, current_line, width*height) |
---|
126 | except Exception as e: |
---|
127 | err_msg = "File incorrectly formatted.\n" |
---|
128 | if str(e).find("list index") != -1: |
---|
129 | err_msg += ("Incorrect number of data points. Expected {} intensity").format(width*height) |
---|
130 | if iflag == 3: |
---|
131 | err_msg += " and error" |
---|
132 | err_msg += " points." |
---|
133 | else: |
---|
134 | err_msg += str(e) |
---|
135 | raise ValueError(err_msg) |
---|
136 | |
---|
137 | # Format data for use with Data2D |
---|
138 | qx = list(qx) * height |
---|
139 | qy = np.array([[y] * width for y in qy]).flatten() |
---|
140 | |
---|
141 | data = Data2D(qx_data=qx, qy_data=qy, data=I, err_data=dI) |
---|
142 | |
---|
143 | return data |
---|