| 1 | import sys |
|---|
| 2 | import itertools |
|---|
| 3 | import operator |
|---|
| 4 | import copy |
|---|
| 5 | import os |
|---|
| 6 | |
|---|
| 7 | import numpy |
|---|
| 8 | import scipy.special |
|---|
| 9 | from mantid.simpleapi import * |
|---|
| 10 | from mantidplot import * |
|---|
| 11 | |
|---|
| 12 | import tube |
|---|
| 13 | from tube_spec import TubeSpec |
|---|
| 14 | from tube_calib_fit_params import TubeCalibFitParams |
|---|
| 15 | from tube_calib import getCalibration |
|---|
| 16 | |
|---|
| 17 | def mean(a): |
|---|
| 18 | return sum(a) / len(a) |
|---|
| 19 | |
|---|
| 20 | def pairwise(iterable): |
|---|
| 21 | """Helper function from: http://docs.python.org/2/library/itertools.html: |
|---|
| 22 | s -> (s0,s1), (s1,s2), (s2, s3), ...""" |
|---|
| 23 | a, b = itertools.tee(iterable) |
|---|
| 24 | next(b, None) |
|---|
| 25 | return itertools.izip(a, b) |
|---|
| 26 | |
|---|
| 27 | def edge_pairs_overlap(edges_pair_a, edges_pair_b): |
|---|
| 28 | """""" |
|---|
| 29 | if edges_pair_a[0] < edges_pair_b[0] < edges_pair_a[1]: |
|---|
| 30 | return True |
|---|
| 31 | if edges_pair_b[0] < edges_pair_a[0] < edges_pair_b[1]: |
|---|
| 32 | return True |
|---|
| 33 | return False |
|---|
| 34 | |
|---|
| 35 | def sort_and_merge_overlapping_edge_pairs(edge_pairs): |
|---|
| 36 | """Algorithm from: http://stackoverflow.com/a/5679899/778572""" |
|---|
| 37 | temp = edge_pairs[0] |
|---|
| 38 | for start, end in sorted([sorted(edge_pair) for edge_pair in edge_pairs]): |
|---|
| 39 | if start <= temp[1]: |
|---|
| 40 | temp[1] = max(temp[1], end) |
|---|
| 41 | else: |
|---|
| 42 | yield tuple(temp) |
|---|
| 43 | temp[0] = start |
|---|
| 44 | temp[1] = end |
|---|
| 45 | yield tuple(temp) |
|---|
| 46 | |
|---|
| 47 | def set_counts_to_one_between_x_range(ws, x_1, x_2): |
|---|
| 48 | """""" |
|---|
| 49 | if x_1 > x_2: x_1, x_2 = x_2, x_1 |
|---|
| 50 | for wsIndex in range(ws.getNumberHistograms()): |
|---|
| 51 | try: |
|---|
| 52 | if x_1 < ws.getDetector(wsIndex).getPos().getX() < x_2: |
|---|
| 53 | ws.dataY(wsIndex)[0] = 1 |
|---|
| 54 | except RuntimeError: |
|---|
| 55 | break |
|---|
| 56 | #pass # Ignore "Detector with ID _____ not found" errors. |
|---|
| 57 | |
|---|
| 58 | def set_counts_to_one_outside_x_range(ws, x_1, x_2): |
|---|
| 59 | """""" |
|---|
| 60 | if x_1 > x_2: x_1, x_2 = x_2, x_1 |
|---|
| 61 | set_counts_to_one_between_x_range(ws, -INF, x_1) |
|---|
| 62 | set_counts_to_one_between_x_range(ws, x_2, INF) |
|---|
| 63 | |
|---|
| 64 | def get_merged_edge_pairs_and_boundaries(edge_pairs): |
|---|
| 65 | boundaries = [-INF] |
|---|
| 66 | edge_pairs_merged = [] |
|---|
| 67 | |
|---|
| 68 | temp = edge_pairs[0] |
|---|
| 69 | |
|---|
| 70 | for start, end in sorted([sorted(edge_pair) for edge_pair in edge_pairs]): |
|---|
| 71 | if start <= temp[1]: |
|---|
| 72 | boundary = start + (temp[1] - start) / 2 |
|---|
| 73 | temp[1] = max(temp[1], end) |
|---|
| 74 | if start != temp[0]: |
|---|
| 75 | boundaries.append(boundary) |
|---|
| 76 | else: |
|---|
| 77 | boundaries.append(temp[1] + (start - temp[1]) / 2) |
|---|
| 78 | edge_pairs_merged.append(tuple(temp)) |
|---|
| 79 | temp[0] = start |
|---|
| 80 | temp[1] = end |
|---|
| 81 | edge_pairs_merged.append(tuple(temp)) |
|---|
| 82 | boundaries.append(INF) |
|---|
| 83 | |
|---|
| 84 | return edge_pairs_merged, boundaries |
|---|
| 85 | |
|---|
| 86 | def get_integrated_workspace(data_file): |
|---|
| 87 | ws_name = os.path.splitext(data_file)[0] |
|---|
| 88 | try: |
|---|
| 89 | ws = mtd[ws_name] |
|---|
| 90 | except: |
|---|
| 91 | print "Loading and integrating data from %s." % data_file |
|---|
| 92 | ws = Load(Filename=data_file, OutputWorkspace=ws_name) |
|---|
| 93 | ws = Integration(ws, OutputWorkspace=ws_name) |
|---|
| 94 | return ws |
|---|
| 95 | |
|---|
| 96 | def multiply_ws_list(ws_list, output_ws_name): |
|---|
| 97 | print "Multiplying workspaces together..." |
|---|
| 98 | it = iter(ws_list) |
|---|
| 99 | total = next(it) |
|---|
| 100 | for element in it: |
|---|
| 101 | total = Multiply(RHSWorkspace=total, LHSWorkspace=element, OutputWorkspace = output_ws_name) |
|---|
| 102 | return total |
|---|
| 103 | |
|---|
| 104 | def get_pixels_from_edges(edges): |
|---|
| 105 | pixels = [] |
|---|
| 106 | for edge in edges: |
|---|
| 107 | pixels.append(int((edge + 0.5207) * 512)) |
|---|
| 108 | return numpy.array(pixels) |
|---|
| 109 | |
|---|
| 110 | def get_tube_name(tube_id): |
|---|
| 111 | # Construct the name of the tube based on the id (0-119) given. |
|---|
| 112 | side = TubeSide.getTubeSide(tube_id) |
|---|
| 113 | tube_side_num = tube_id / 2 |
|---|
| 114 | return "rear-detector/" + side + str(tube_side_num) |
|---|
| 115 | |
|---|
| 116 | def get_tube_data(tube_id, ws): |
|---|
| 117 | tube_name = get_tube_name(tube_id) |
|---|
| 118 | |
|---|
| 119 | # Piggy-back the TubeSpec class from Karl's Calibration code so that dealing with tubes is easier than interrogating the IDF ourselves. |
|---|
| 120 | tube_spec = TubeSpec(ws) |
|---|
| 121 | tube_spec.setTubeSpecByString(tube_name) |
|---|
| 122 | assert tube_spec.getNumTubes() == 1 |
|---|
| 123 | tube_ws_index_list = tube_spec.getTube(0)[0] |
|---|
| 124 | assert len(tube_ws_index_list) == 512 |
|---|
| 125 | |
|---|
| 126 | # Return an array of all counts for the tube. |
|---|
| 127 | return numpy.array([ws.dataY(ws_index)[0] for ws_index in tube_ws_index_list]) |
|---|
| 128 | |
|---|
| 129 | def get_tube_edge_pixels(tube_id, ws, cutoff, first_pixel=0, last_pixel=sys.maxint): |
|---|
| 130 | count_data = get_tube_data(tube_id, ws) |
|---|
| 131 | |
|---|
| 132 | if count_data[first_pixel] < cutoff: |
|---|
| 133 | up_edge = True |
|---|
| 134 | else: |
|---|
| 135 | up_edge = False |
|---|
| 136 | |
|---|
| 137 | for i, count in enumerate(count_data[first_pixel:last_pixel+1]): |
|---|
| 138 | pixel = first_pixel + i |
|---|
| 139 | if pixel > last_pixel: |
|---|
| 140 | break |
|---|
| 141 | if up_edge: |
|---|
| 142 | if count >= cutoff: |
|---|
| 143 | up_edge = False |
|---|
| 144 | yield pixel |
|---|
| 145 | else: |
|---|
| 146 | if count < cutoff: |
|---|
| 147 | up_edge = True |
|---|
| 148 | yield pixel |
|---|
| 149 | |
|---|
| 150 | def compile_param_table(tube_id, peaks): |
|---|
| 151 | # Dirty hack we need to replace with something better. |
|---|
| 152 | full_table = CreateEmptyTableWorkspace(OutputWorkspace="ParamTable_Tube" + str(tube_id)) |
|---|
| 153 | full_table.addColumn("int", "Peak #") |
|---|
| 154 | full_table.addColumn("float", "A") |
|---|
| 155 | full_table.addColumn("float", "B") |
|---|
| 156 | full_table.addColumn("float", "C") |
|---|
| 157 | full_table.addColumn("float", "D") |
|---|
| 158 | base_name = "CalibPoint_Parameters_tube%i_peak_%i" |
|---|
| 159 | table_names = [base_name % (tube_id, peak) for peak in range(peaks)] |
|---|
| 160 | for peak, table_name in enumerate(table_names): |
|---|
| 161 | table = mtd[table_name] |
|---|
| 162 | row = [ |
|---|
| 163 | peak, |
|---|
| 164 | table.cell("Value", 0), |
|---|
| 165 | table.cell("Value", 1), |
|---|
| 166 | table.cell("Value", 2), |
|---|
| 167 | table.cell("Value", 3) |
|---|
| 168 | ] |
|---|
| 169 | full_table.addRow(row) |
|---|
| 170 | |
|---|
| 171 | def cleanup_param_tables(tube_id, peaks): |
|---|
| 172 | # Dirty hack we need to replace with something better. |
|---|
| 173 | base_name = "CalibPoint_Parameters_tube%i_peak_%i" |
|---|
| 174 | table_names = [base_name % (tube_id, peak) for peak in range(peaks)] |
|---|
| 175 | for table_name in table_names: |
|---|
| 176 | table = mtd[table_name] |
|---|
| 177 | table.delete() |
|---|
| 178 | |
|---|
| 179 | BACKGROUND = 10 |
|---|
| 180 | |
|---|
| 181 | class SANS2DEndErfc(IFunction1D): |
|---|
| 182 | def init(self): |
|---|
| 183 | self.declareParameter("A", 350.0) |
|---|
| 184 | self.declareParameter("B", 50.0) |
|---|
| 185 | self.declareParameter("C", 6.0) |
|---|
| 186 | self.declareParameter("D", BACKGROUND) |
|---|
| 187 | |
|---|
| 188 | def function1D(self, xvals): |
|---|
| 189 | a = self.getParameterValue("A") |
|---|
| 190 | b = self.getParameterValue("B") |
|---|
| 191 | c = self.getParameterValue("C") |
|---|
| 192 | d = self.getParameterValue("D") |
|---|
| 193 | |
|---|
| 194 | out = a * scipy.special.erfc((b - xvals) / c) + d |
|---|
| 195 | if a < 0: |
|---|
| 196 | out = -2*a * out |
|---|
| 197 | |
|---|
| 198 | return out |
|---|
| 199 | |
|---|
| 200 | def setActiveParameter(self, index, value): |
|---|
| 201 | # Heavy-handed way to constrain background. |
|---|
| 202 | if self.parameterName(index) == "D" and value < 0.0: |
|---|
| 203 | self.setParameter(index, 0.0, False) |
|---|
| 204 | elif self.parameterName(index) == "D" and value > BACKGROUND: |
|---|
| 205 | self.setParameter(index, BACKGROUND, False) |
|---|
| 206 | else: |
|---|
| 207 | self.setParameter(index, value, False) |
|---|
| 208 | |
|---|
| 209 | # Required to have Mantid recognise the new function |
|---|
| 210 | FunctionFactory.subscribe(SANS2DEndErfc) |
|---|
| 211 | |
|---|
| 212 | INF = sys.float_info.max |
|---|
| 213 | TUBE_OFFSET = 0.003 |
|---|
| 214 | |
|---|
| 215 | # The number of counts past which we class something as an edge. This is quite sensitive to change, since we sometimes end up picking |
|---|
| 216 | # up edges more than once, (e.g. we might see an edge at pixels 207 and 209, obviously due to the counts dipping back down below the |
|---|
| 217 | # threshold at pixel 206) which we then have to remove using ignore_guesses. So, if THRESHOLD is changed you're probably going |
|---|
| 218 | # to have to change the contents of ignore_guesses for any affected tubes. |
|---|
| 219 | THRESHOLD = 500 |
|---|
| 220 | |
|---|
| 221 | class TubeSide: |
|---|
| 222 | LEFT = "left" |
|---|
| 223 | RIGHT = "right" |
|---|
| 224 | @classmethod |
|---|
| 225 | def getTubeSide(cls, tube_id): |
|---|
| 226 | if tube_id % 2 == 0: |
|---|
| 227 | return TubeSide.LEFT |
|---|
| 228 | else: |
|---|
| 229 | return TubeSide.RIGHT |
|---|
| 230 | |
|---|
| 231 | # second runs, mostly with M4 removed |
|---|
| 232 | # 23088-add really was added, the hst files are single runs converted to histogram, so can merge with the added file |
|---|
| 233 | data_files = ["SANS2D00023098-hst.nxs","SANS2D00023097-hst.nxs","SANS2D00023096-hst.nxs","SANS2D00023095-hst.nxs","SANS2D00023094-hst.nxs", |
|---|
| 234 | "SANS2D00023093-hst.nxs","SANS2D00023092-hst.nxs","SANS2D00023091-hst.nxs", |
|---|
| 235 | "SANS2D00023088-add.nxs", |
|---|
| 236 | "SANS2D00023084-hst.nxs"] |
|---|
| 237 | |
|---|
| 238 | known_edge_pairs = [ |
|---|
| 239 | [-0.415366832, -0.377084653], |
|---|
| 240 | [-0.334772772, -0.296490594], |
|---|
| 241 | [-0.254178713, -0.215896535], |
|---|
| 242 | [-0.163510396, -0.125228218], |
|---|
| 243 | [-0.082916337, -0.044634158], |
|---|
| 244 | [0.00775198, 0.046034158], |
|---|
| 245 | [0.098420297, 0.136702475], |
|---|
| 246 | [0.189088614, 0.227370792], |
|---|
| 247 | [0.279756931, 0.318039109], |
|---|
| 248 | [0.370425248, 0.408707426] |
|---|
| 249 | ] |
|---|
| 250 | |
|---|
| 251 | assert len(known_edge_pairs) == len(data_files) |
|---|
| 252 | |
|---|
| 253 | ignore_guesses = { |
|---|
| 254 | 69 : [20, 21], # right34 |
|---|
| 255 | 68 : [20, 21], # left34 |
|---|
| 256 | 70 : [20], # left35 |
|---|
| 257 | 118 : [12, 13], # left59 |
|---|
| 258 | } |
|---|
| 259 | edges_not_to_fit = { |
|---|
| 260 | 47 : [10, 11], # right23 |
|---|
| 261 | 49 : [10, 11], # right24 |
|---|
| 262 | 51 : [10, 11], # right25 |
|---|
| 263 | 53 : [10, 11], # right26 |
|---|
| 264 | |
|---|
| 265 | 67 : [19], # right33 |
|---|
| 266 | |
|---|
| 267 | 44 : [10, 11], # left22 |
|---|
| 268 | 46 : [10, 11], # left23 |
|---|
| 269 | 48 : [10, 11], # left24 |
|---|
| 270 | 50 : [10, 11], # left25 |
|---|
| 271 | 52 : [10, 11], # left26 |
|---|
| 272 | |
|---|
| 273 | 68 : [19], # left34 |
|---|
| 274 | } |
|---|
| 275 | |
|---|
| 276 | # Array indices of shadows (edge pairs) to remove. |
|---|
| 277 | shadows_to_remove = [] |
|---|
| 278 | |
|---|
| 279 | for shadow in reversed(sorted(shadows_to_remove)): |
|---|
| 280 | del known_edge_pairs[shadow] |
|---|
| 281 | del data_files[shadow] |
|---|
| 282 | |
|---|
| 283 | known_edge_pairs = numpy.array(known_edge_pairs) |
|---|
| 284 | |
|---|
| 285 | ws_list = [get_integrated_workspace(data_file) for data_file in data_files] |
|---|
| 286 | |
|---|
| 287 | known_left_edge_pairs = copy.copy(known_edge_pairs) |
|---|
| 288 | known_right_edge_pairs = copy.copy(known_edge_pairs + TUBE_OFFSET) |
|---|
| 289 | |
|---|
| 290 | _, boundaries = get_merged_edge_pairs_and_boundaries(known_edge_pairs) |
|---|
| 291 | known_left_edges, _ = get_merged_edge_pairs_and_boundaries(known_left_edge_pairs) |
|---|
| 292 | known_right_edges, _ = get_merged_edge_pairs_and_boundaries(known_right_edge_pairs) |
|---|
| 293 | |
|---|
| 294 | for ws, (boundary_start, boundary_end) in zip(ws_list, pairwise(boundaries)): |
|---|
| 295 | print "Isolating shadow in %s between boundaries %g and %g." % (str(ws), boundary_start, boundary_end) |
|---|
| 296 | set_counts_to_one_outside_x_range(ws, boundary_start, boundary_end) |
|---|
| 297 | |
|---|
| 298 | result_ws_name = "result" |
|---|
| 299 | |
|---|
| 300 | multiply_ws_list(ws_list, result_ws_name) |
|---|
| 301 | |
|---|
| 302 | result = mtd[result_ws_name] |
|---|
| 303 | original = CloneWorkspace(InputWorkspace=result_ws_name, OutputWorkspace="original") |
|---|
| 304 | |
|---|
| 305 | known_edges_left = list(itertools.chain.from_iterable(known_left_edges)) |
|---|
| 306 | known_edges_right = list(itertools.chain.from_iterable(known_right_edges)) |
|---|
| 307 | |
|---|
| 308 | margin=10 |
|---|
| 309 | |
|---|
| 310 | caltable = None |
|---|
| 311 | |
|---|
| 312 | failed_pixel_guesses = [] |
|---|
| 313 | pixel_guesses = [] |
|---|
| 314 | |
|---|
| 315 | for tube_id in range(120): |
|---|
| 316 | tube_name = get_tube_name(tube_id) |
|---|
| 317 | print "\n==================================================" |
|---|
| 318 | print "ID = %i, Name = \"%s\"" % (tube_id, tube_name) |
|---|
| 319 | if TubeSide.getTubeSide(tube_id) == TubeSide.LEFT: |
|---|
| 320 | known_edges = copy.copy(known_edges_left) |
|---|
| 321 | else: |
|---|
| 322 | known_edges = copy.copy(known_edges_right) |
|---|
| 323 | |
|---|
| 324 | guessed_pixels = list(get_tube_edge_pixels(tube_id, result, THRESHOLD, 20, 482)) |
|---|
| 325 | |
|---|
| 326 | print guessed_pixels |
|---|
| 327 | print known_edges |
|---|
| 328 | |
|---|
| 329 | # Remove the pixel guesses that don't correspond to Cd shadows: |
|---|
| 330 | if tube_id in ignore_guesses: |
|---|
| 331 | print "Removing guesses", list(reversed(sorted(ignore_guesses[tube_id]))) |
|---|
| 332 | for index in reversed(sorted(ignore_guesses[tube_id])): |
|---|
| 333 | del guessed_pixels[index] |
|---|
| 334 | |
|---|
| 335 | assert len(guessed_pixels) == len(known_edges) |
|---|
| 336 | |
|---|
| 337 | # Store the guesses for printing out later, along with the tube id and name. |
|---|
| 338 | pixel_guesses.append(guessed_pixels) |
|---|
| 339 | |
|---|
| 340 | # Remove the edges that have been altered by the presence of the beam stop and arm. |
|---|
| 341 | if tube_id in edges_not_to_fit: |
|---|
| 342 | for index in reversed(sorted(edges_not_to_fit[tube_id])): |
|---|
| 343 | del guessed_pixels[index] |
|---|
| 344 | del known_edges[index] |
|---|
| 345 | |
|---|
| 346 | fitPar = TubeCalibFitParams(guessed_pixels, outEdge=10.0, inEdge=10.0) |
|---|
| 347 | funcForm = [2]*len(guessed_pixels) |
|---|
| 348 | |
|---|
| 349 | if caltable: |
|---|
| 350 | caltable = tube.calibrate( |
|---|
| 351 | result, |
|---|
| 352 | tube_name, |
|---|
| 353 | numpy.array(known_edges), |
|---|
| 354 | funcForm, |
|---|
| 355 | rangeList=[0], |
|---|
| 356 | plotTube=[0], |
|---|
| 357 | margin=margin, |
|---|
| 358 | fitPar=fitPar, |
|---|
| 359 | calibTable=caltable) |
|---|
| 360 | else: |
|---|
| 361 | caltable = tube.calibrate( |
|---|
| 362 | result, |
|---|
| 363 | tube_name, |
|---|
| 364 | numpy.array(known_edges), |
|---|
| 365 | funcForm, |
|---|
| 366 | rangeList=[0], |
|---|
| 367 | plotTube=[0], |
|---|
| 368 | margin=margin, |
|---|
| 369 | fitPar=fitPar) |
|---|
| 370 | |
|---|
| 371 | #compile_param_table(tube_id, len(known_edges)) |
|---|
| 372 | #cleanup_param_tables(tube_id, len(known_edges)) |
|---|
| 373 | |
|---|
| 374 | ApplyCalibration(result, caltable) |
|---|
| 375 | |
|---|
| 376 | # Show result in instrument view. (See http://www.mantidproject.org/MantidPlot:_Instrument_View) |
|---|
| 377 | inst_win = getInstrumentView(result_ws_name) |
|---|
| 378 | inst_win.show() |
|---|
| 379 | |
|---|
| 380 | render = inst_win.getTab(InstrumentWindow.RENDER) |
|---|
| 381 | render.setSurfaceType(InstrumentWindow.CYLINDRICAL_Y) |
|---|
| 382 | render.setRange(0.0,900.0) |
|---|
| 383 | |
|---|
| 384 | tree = inst_win.getTab(InstrumentWindow.TREE) |
|---|
| 385 | tree.selectComponentByName("rear-detector") |
|---|
| 386 | |
|---|
| 387 | left = pixel_guesses[0::2] |
|---|
| 388 | right = pixel_guesses[1::2] |
|---|
| 389 | |
|---|
| 390 | left_avg = numpy.array(map(mean, zip(*left))) |
|---|
| 391 | right_avg = numpy.array(map(mean, zip(*right))) |
|---|
| 392 | |
|---|
| 393 | print left_avg |
|---|
| 394 | print right_avg |
|---|
| 395 | print right_avg - left_avg |
|---|
| 396 | |
|---|