def test_legend_uses_label_if_first_axis_is_numeric_axis(self): ws = CreateWorkspace([1], [1], NSpec=1) axis = mantid.api.NumericAxis.create(1) ws.replaceAxis(1, axis) ws.getAxis(1).setValue(0, 50) index, dist, kwargs = funcs.get_wksp_index_dist_and_label(ws, specNum=1) self.assertEqual(kwargs['label'], 'ws: 50')
def test_set_unicode_unit_label(self): """ Set the label of the x-axis using ascii only with a non-ascii character and make sure it's handled properly. """ ws = CreateWorkspace(DataX=[0, 1, 2], DataY=[3, 7, 5], DataE=[0.2, 0.3, 0.1], NSpec=1) label_unit = ws.getAxis(0).setUnit("Label") microseconds = "\u00B5s" # Second argument will implicitly call the ascii only constructor of UnitLabel. # We are intentionally passing a non-ascii string to try and break it. label_unit.setLabel("Time", microseconds) model_type = MatrixWorkspaceTableViewModelType.y model = MatrixWorkspaceTableViewModel(ws, model_type) header = model.headerData(0, Qt.Horizontal, Qt.DisplayRole) # Header should contain the microseconds unicode string. self.assertTrue(microseconds in header)
def PyExec(self): """ Alg execution. """ instrument = self.getProperty(INSTRUMENT_PROP).value run_number = self.getProperty(RUN_NUM_PROP).value fit_deadtime = self.getProperty(FIT_DEADTIME_PROP).value fix_phases = self.getProperty(FIX_PHASES_PROP).value default_level = self.getProperty(DEFAULT_LEVEL).value sigma_looseness = self.getProperty(SIGMA_LOOSENESS_PROP).value groupings_file = self.getProperty(GROUPINGS_PROP).value in_phases_file = self.getProperty(PHASES_PROP).value in_deadtimes_file = self.getProperty(DEADTIMES_PROP).value out_phases_file = self.getProperty(PHASES_RESULT_PROP).value out_deadtimes_file = self.getProperty(DEADTIMES_RESULT_PROP).value isis = config.getFacility('ISIS') padding = isis.instrument(instrument).zeroPadding(0) run_name = instrument + str(run_number).zfill(padding) try: run_number = int(run_number) except: raise RuntimeError("'%s' is not an integer run number." % run_number) try: run_file_path = FileFinder.findRuns(run_name)[0] except: raise RuntimeError("Unable to find file for run %i" % run_number) if groupings_file == "": groupings_file = DEFAULT_GROUPINGS_FILENAME % instrument # Load data and other info from input files. def temp_hidden_ws_name(): """Generate a unique name for a temporary, hidden workspace.""" selection = string.ascii_lowercase + string.ascii_uppercase + string.digits return '__temp_MaxEnt_' + ''.join( random.choice(selection) for _ in range(20)) input_data_ws_name = temp_hidden_ws_name() LoadMuonNexus(Filename=run_file_path, OutputWorkspace=input_data_ws_name) input_data_ws = mtd[input_data_ws_name] if isinstance(input_data_ws, WorkspaceGroup): Logger.get("MaxEnt").warning( "Multi-period data is not currently supported. Just using first period." ) input_data_ws = input_data_ws[0] groupings_ws_name = temp_hidden_ws_name() LoadDetectorsGroupingFile(InputFile=groupings_file, OutputWorkspace=groupings_ws_name) groupings_ws = mtd[groupings_ws_name] def yield_floats_from_file(path): """Given a path to a file with a float on each line, will return the floats one at a time. Throws otherwise. Strips whitespace and ignores empty lines.""" with open(path, 'r') as f: for i, line in enumerate(line.strip() for line in f): if line == "": continue try: yield float(line) except: raise RuntimeError( "Parsing error in '%s': Line %d: '%s'." % (path, i, line)) input_phases = np.array(list(yield_floats_from_file(in_phases_file))) input_phases_size = len(input_phases) input_deadtimes = np.array( list(yield_floats_from_file(in_deadtimes_file))) input_deadtimes_size = len(input_deadtimes) n_bins = input_data_ws.blocksize() n_detectors = input_data_ws.getNumberHistograms() def time_value_to_time_channel_index(value): """Given a time value, will return the index of the time channel in which the value falls.""" bin_width = input_data_ws.readX(0)[1] - input_data_ws.readX(0)[0] diff = value - input_data_ws.readX(0)[0] return int(diff / bin_width) # Mantid corrects for time zero on loading, so we want to find the actual channels # where 0.0 occurs, and where we have values of 0.1 onwards. time_zero_channel = time_value_to_time_channel_index(0.0) first_good_channel = time_value_to_time_channel_index(0.1) input_data = np.concatenate( [input_data_ws.readY(i) for i in range(n_detectors)]) groupings = [ groupings_ws.readY(row)[0] for row in range(groupings_ws.getNumberHistograms()) ] groupings = map(int, groupings) n_groups = len(set(groupings)) # Cleanup. input_data_ws.delete() groupings_ws.delete() # We're faced with the problem of providing more than a dozen parameters to # the Fortran, which can be a bit messy (especially on the Fortran side of # things where we need to make "Cf2py" declarations). A cleaner way of # doing this is to simply pass in a few callbacks -- one for each input # type -- and have the Fortran provide the name of the variable it wants # to the callback. The callback will then look up the corresponding value # and feed it back to the Fortran. # # We also have a callback for printing to the results log. self.int_vars = { "RunNo": run_number, "frames": FRAMES, "res": RES, "Tzeroch": time_zero_channel, "firstgoodch": first_good_channel, "ptstofit": POINTS_TO_FIT, "histolen": n_bins, "nhisto": n_detectors, "n_groups": n_groups, } self.float_vars = { "deflevel": default_level, "sigloose": sigma_looseness, } self.bool_vars = { "fixphase": fix_phases, "fitdt": fit_deadtime, } self._assert_map_values_are_of_expected_type() def lookup(par_name, par_map, default): """The basis of the callbacks passed to the Fortran. Given a parameter name it will consult the appropriate variable map, and return the corresponding value of the parameter. Else return a default and log a warning if a parameter with the name does not exist.""" par_name = par_name.strip() if par_name in par_map: return par_map[par_name] msg = """WARNING: tried to find a value for parameter with name %s but could not find one. Default of \"%s\" provided.""" % (par_name, default) Logger.get("MaxEnt").warning(msg) return default def log(priority, message): """Log the given message with given priority.""" try: logger = getattr(Logger.get("MaxEnt"), priority.lower()) except AttributeError: # If we don't recognise the priority, use warning() as a default. logger = getattr(Logger.get("MaxEnt"), "warning") logger(message) return True # The Fortran expects arrays to be of a certain size, so any arrays that # aren't big enough need to be padded. input_phases = self._pad_to_length_with_zeros(input_phases, MAX_HISTOS) input_deadtimes = self._pad_to_length_with_zeros( input_deadtimes, MAX_HISTOS) input_data = self._pad_to_length_with_zeros(input_data, MAX_INPUT_DATA_SIZE) groupings = self._pad_to_length_with_zeros(groupings, MAX_HISTOS) # TODO: Return the contents of "NNNNN.max", instead of writing to file. f_out, fchan_out, output_deadtimes, output_phases, chi_sq = maxent.mantid_maxent( # Input data and other info: input_data, groupings, input_deadtimes, input_phases, # Variable-lookup callbacks: lambda par_name: lookup(par_name, self.int_vars, 0), lambda par_name: lookup(par_name, self.float_vars, 0.0), lambda par_name: lookup(par_name, self.bool_vars, False), # Callback for logging: log) def write_items_to_file(path, items): """Given a path to a file and a list of items, will write the items to the file, one on each line.""" with open(path, 'w') as f: for item in items: f.write(str(item) + "\n") # Chop the padded outputs back down to the correct size. output_phases = output_phases[:input_phases_size] output_deadtimes = output_deadtimes[:input_deadtimes_size] input_phases = input_phases[:input_phases_size] input_deadtimes = input_deadtimes[:input_deadtimes_size] fchan_out = fchan_out[:n_bins] f_out = f_out[:n_bins] write_items_to_file(out_phases_file, output_phases) write_items_to_file(out_deadtimes_file, output_deadtimes) log_output = "\nDead times in:\n" + str(input_deadtimes) + "\n" +\ "\nDead times out:\n" + str(output_deadtimes) + "\n" +\ "\nPhases in:\n" + str(input_phases) + "\n" +\ "\nPhases out:\n" + str(output_phases) + "\n" + \ "\nGroupings:\n" + str(groupings) + "\n" +\ "\nChi Squared:\n" + str(chi_sq) + "\n" +\ "\nInput variables:\n" for type_map in self.int_vars, self.float_vars, self.bool_vars: for name, value in type_map.items(): log_output += str(name) + " = " + str(value) + "\n" Logger.get("MaxEnt").notice(log_output) # Generate our own output ws name if the user has not provided one. out_ws_name = self.getPropertyValue(OUT_WS_PROP) if out_ws_name == "": out_ws_name = run_name + "; MaxEnt" self.setPropertyValue(OUT_WS_PROP, out_ws_name) out_ws = CreateWorkspace(OutputWorkspace=out_ws_name, DataX=fchan_out[:n_bins], DataY=f_out[:n_bins]) self.setProperty(OUT_WS_PROP, out_ws) # MaxEnt inputs table. input_table_name = run_name + "; MaxEnt Input" input_table = CreateEmptyTableWorkspace( OutputWorkspace=input_table_name) input_table.addColumn("str", "Name") input_table.addColumn("str", "Value") inputs = itertools.chain(self.int_vars.items(), self.float_vars.items(), self.bool_vars.items()) for name, value in inputs: input_table.addRow([str(name), str(value)]) # Deadtimes and phases input/output table. dead_phases_table_name = run_name + "; MaxEnt Deadtimes & Phases" dead_phases_table = CreateEmptyTableWorkspace( OutputWorkspace=dead_phases_table_name) for column_name in "Deadtimes In", "Deadtimes Out", "Phases In", "Phases Out": dead_phases_table.addColumn("double", column_name) for row in zip(input_deadtimes, output_deadtimes, input_phases, output_phases): dead_phases_table.addRow(list(map(float, row))) # Chi-squared output table. chisq_table_name = run_name + "; MaxEnt Chi^2" chisq_table = CreateEmptyTableWorkspace( OutputWorkspace=chisq_table_name) chisq_table.addColumn("int", "Cycle") for iteration in range(10): chisq_table.addColumn("double", "Iter " + str(iteration + 1)) for cycle, data in enumerate(chi_sq): chisq_table.addRow([cycle + 1] + list(map(float, data))) all_output_ws = [ input_table_name, dead_phases_table_name, chisq_table_name, out_ws_name ] # The output workspaces of this algorithm belong in the same groups # that are created by the muon interface. If the appropriate group # doesn't exist already then it needs to be created. if not run_name in mtd: GroupWorkspaces(InputWorkspaces=all_output_ws, OutputWorkspace=run_name) else: group = mtd[run_name] for output_ws in all_output_ws: if not group.contains(output_ws): group.add(output_ws) out_ws.getAxis(0).getUnit().setLabel("Field", "G") out_ws.setYUnitLabel("P(B)") if INSIDE_MANTIDPLOT: mantidplot.plotSpectrum(out_ws, 0)
def PyExec(self): """ Alg execution. """ instrument = self.getProperty(INSTRUMENT_PROP).value run_number = self.getProperty(RUN_NUM_PROP).value fit_deadtime = self.getProperty(FIT_DEADTIME_PROP).value fix_phases = self.getProperty(FIX_PHASES_PROP).value default_level = self.getProperty(DEFAULT_LEVEL).value sigma_looseness = self.getProperty(SIGMA_LOOSENESS_PROP).value groupings_file = self.getProperty(GROUPINGS_PROP).value in_phases_file = self.getProperty(PHASES_PROP).value in_deadtimes_file = self.getProperty(DEADTIMES_PROP).value out_phases_file = self.getProperty(PHASES_RESULT_PROP).value out_deadtimes_file = self.getProperty(DEADTIMES_RESULT_PROP).value isis = config.getFacility('ISIS') padding = isis.instrument(instrument).zeroPadding(0) run_name = instrument + str(run_number).zfill(padding) try: run_number = int(run_number) except: raise RuntimeError("'%s' is not an integer run number." % run_number) try: run_file_path = FileFinder.findRuns(run_name)[0] except: raise RuntimeError("Unable to find file for run %i" % run_number) if groupings_file == "": groupings_file = DEFAULT_GROUPINGS_FILENAME % instrument # Load data and other info from input files. def temp_hidden_ws_name(): """Generate a unique name for a temporary, hidden workspace.""" selection = string.ascii_lowercase + string.ascii_uppercase + string.digits return '__temp_MaxEnt_' + ''.join(random.choice(selection) for _ in range(20)) input_data_ws_name = temp_hidden_ws_name() LoadMuonNexus(Filename=run_file_path, OutputWorkspace=input_data_ws_name) input_data_ws = mtd[input_data_ws_name] if isinstance(input_data_ws, WorkspaceGroup): Logger.get("MaxEnt").warning("Multi-period data is not currently supported. Just using first period.") input_data_ws = input_data_ws[0] groupings_ws_name = temp_hidden_ws_name() LoadDetectorsGroupingFile(InputFile=groupings_file, OutputWorkspace=groupings_ws_name) groupings_ws = mtd[groupings_ws_name] def yield_floats_from_file(path): """Given a path to a file with a float on each line, will return the floats one at a time. Throws otherwise. Strips whitespace and ignores empty lines.""" with open(path, 'r') as f: for i, line in enumerate(line.strip() for line in f): if line == "": continue try: yield float(line) except: raise RuntimeError("Parsing error in '%s': Line %d: '%s'." % (path, i, line)) input_phases = np.array(list(yield_floats_from_file(in_phases_file))) input_phases_size = len(input_phases) input_deadtimes = np.array(list(yield_floats_from_file(in_deadtimes_file))) input_deadtimes_size = len(input_deadtimes) n_bins = input_data_ws.blocksize() n_detectors = input_data_ws.getNumberHistograms() def time_value_to_time_channel_index(value): """Given a time value, will return the index of the time channel in which the value falls.""" bin_width = input_data_ws.readX(0)[1] - input_data_ws.readX(0)[0] diff = value - input_data_ws.readX(0)[0] return int(diff / bin_width) # Mantid corrects for time zero on loading, so we want to find the actual channels # where 0.0 occurs, and where we have values of 0.1 onwards. time_zero_channel = time_value_to_time_channel_index(0.0) first_good_channel = time_value_to_time_channel_index(0.1) input_data = np.concatenate([input_data_ws.readY(i) for i in range(n_detectors)]) groupings = [groupings_ws.readY(row)[0] for row in range(groupings_ws.getNumberHistograms())] groupings = map(int, groupings) n_groups = len(set(groupings)) # Cleanup. input_data_ws.delete() groupings_ws.delete() # We're faced with the problem of providing more than a dozen parameters to # the Fortran, which can be a bit messy (especially on the Fortran side of # things where we need to make "Cf2py" declarations). A cleaner way of # doing this is to simply pass in a few callbacks -- one for each input # type -- and have the Fortran provide the name of the variable it wants # to the callback. The callback will then look up the corresponding value # and feed it back to the Fortran. # # We also have a callback for printing to the results log. self.int_vars = { "RunNo" : run_number, "frames" : FRAMES, "res" : RES, "Tzeroch" : time_zero_channel, "firstgoodch" : first_good_channel, "ptstofit" : POINTS_TO_FIT, "histolen" : n_bins, "nhisto" : n_detectors, "n_groups" : n_groups, } self.float_vars = { "deflevel" : default_level, "sigloose" : sigma_looseness, } self.bool_vars = { "fixphase" : fix_phases, "fitdt" : fit_deadtime, } self._assert_map_values_are_of_expected_type() def lookup(par_name, par_map, default): """The basis of the callbacks passed to the Fortran. Given a parameter name it will consult the appropriate variable map, and return the corresponding value of the parameter. Else return a default and log a warning if a parameter with the name does not exist.""" par_name = par_name.strip() if par_name in par_map: return par_map[par_name] msg = """WARNING: tried to find a value for parameter with name %s but could not find one. Default of \"%s\" provided.""" % (par_name, default) Logger.get("MaxEnt").warning(msg) return default def log(priority, message): """Log the given message with given priority.""" try: logger = getattr(Logger.get("MaxEnt"), priority.lower()) except AttributeError: # If we don't recognise the priority, use warning() as a default. logger = getattr(Logger.get("MaxEnt"), "warning") logger(message) return True # The Fortran expects arrays to be of a certain size, so any arrays that # aren't big enough need to be padded. input_phases = self._pad_to_length_with_zeros(input_phases, MAX_HISTOS) input_deadtimes = self._pad_to_length_with_zeros(input_deadtimes, MAX_HISTOS) input_data = self._pad_to_length_with_zeros(input_data, MAX_INPUT_DATA_SIZE) groupings = self._pad_to_length_with_zeros(groupings, MAX_HISTOS) # TODO: Return the contents of "NNNNN.max", instead of writing to file. f_out, fchan_out, output_deadtimes, output_phases, chi_sq = maxent.mantid_maxent( # Input data and other info: input_data, groupings, input_deadtimes, input_phases, # Variable-lookup callbacks: lambda par_name: lookup(par_name, self.int_vars, 0), lambda par_name: lookup(par_name, self.float_vars, 0.0), lambda par_name: lookup(par_name, self.bool_vars, False), # Callback for logging: log ) def write_items_to_file(path, items): """Given a path to a file and a list of items, will write the items to the file, one on each line.""" with open(path, 'w') as f: for item in items: f.write(str(item) + "\n") # Chop the padded outputs back down to the correct size. output_phases = output_phases[:input_phases_size] output_deadtimes = output_deadtimes[:input_deadtimes_size] input_phases = input_phases[:input_phases_size] input_deadtimes = input_deadtimes[:input_deadtimes_size] fchan_out = fchan_out[:n_bins] f_out = f_out[:n_bins] write_items_to_file(out_phases_file, output_phases) write_items_to_file(out_deadtimes_file, output_deadtimes) log_output = "\nDead times in:\n" + str(input_deadtimes) + "\n" +\ "\nDead times out:\n" + str(output_deadtimes) + "\n" +\ "\nPhases in:\n" + str(input_phases) + "\n" +\ "\nPhases out:\n" + str(output_phases) + "\n" + \ "\nGroupings:\n" + str(groupings) + "\n" +\ "\nChi Squared:\n" + str(chi_sq) + "\n" +\ "\nInput variables:\n" for type_map in self.int_vars, self.float_vars, self.bool_vars: for name, value in type_map.items(): log_output += str(name) + " = " + str(value) + "\n" Logger.get("MaxEnt").notice(log_output) # Generate our own output ws name if the user has not provided one. out_ws_name = self.getPropertyValue(OUT_WS_PROP) if out_ws_name == "": out_ws_name = run_name + "; MaxEnt" self.setPropertyValue(OUT_WS_PROP, out_ws_name) out_ws = CreateWorkspace(OutputWorkspace=out_ws_name, DataX=fchan_out[:n_bins], DataY=f_out[:n_bins]) self.setProperty(OUT_WS_PROP, out_ws) # MaxEnt inputs table. input_table_name = run_name + "; MaxEnt Input" input_table = CreateEmptyTableWorkspace(OutputWorkspace = input_table_name) input_table.addColumn("str", "Name") input_table.addColumn("str", "Value") inputs = itertools.chain(self.int_vars.items(), self.float_vars.items(), self.bool_vars.items()) for name, value in inputs: input_table.addRow([str(name), str(value)]) # Deadtimes and phases input/output table. dead_phases_table_name = run_name + "; MaxEnt Deadtimes & Phases" dead_phases_table = CreateEmptyTableWorkspace(OutputWorkspace = dead_phases_table_name) for column_name in "Deadtimes In", "Deadtimes Out", "Phases In", "Phases Out": dead_phases_table.addColumn("double", column_name) for row in zip(input_deadtimes, output_deadtimes, input_phases, output_phases): dead_phases_table.addRow(list(map(float, row))) # Chi-squared output table. chisq_table_name = run_name + "; MaxEnt Chi^2" chisq_table = CreateEmptyTableWorkspace(OutputWorkspace = chisq_table_name) chisq_table.addColumn("int", "Cycle") for iteration in range(10): chisq_table.addColumn("double", "Iter " + str(iteration + 1)) for cycle, data in enumerate(chi_sq): chisq_table.addRow([cycle + 1] + list(map(float,data))) all_output_ws = [input_table_name, dead_phases_table_name, chisq_table_name, out_ws_name] # The output workspaces of this algorithm belong in the same groups # that are created by the muon interface. If the appropriate group # doesn't exist already then it needs to be created. if not run_name in mtd: GroupWorkspaces(InputWorkspaces = all_output_ws, OutputWorkspace = run_name) else: group = mtd[run_name] for output_ws in all_output_ws: if not group.contains(output_ws): group.add(output_ws) out_ws.getAxis(0).getUnit().setLabel("Field", "G") out_ws.setYUnitLabel("P(B)") if INSIDE_MANTIDPLOT: mantidplot.plotSpectrum(out_ws, 0)