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