diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py new file mode 100644 index 0000000000000000000000000000000000000000..3979814f91c1314cdcffa186061997875fb3c8ac --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/ISISIndirectEnergyTransfer.py @@ -0,0 +1,842 @@ +from mantid.kernel import * +from mantid.api import * +from mantid.simpleapi import * +import mantid +import os +import string +import numpy as np + + +_str_or_none = lambda s: s if s != '' else None +_float_or_none = lambda i: float(i) if i != '' else None +_elems_or_none = lambda l: l if len(l) != 0 else None + + +class ISISIndirectEnergyTransfer(DataProcessorAlgorithm): + + def category(self): + return 'Workflow\\Inelastic;PythonAlgorithms;Inelastic' + + + def summary(self): + return 'Runs an energy transfer reduction for an inelastic indirect geometry instrument.' + + + def PyInit(self): + # Input properties + self.declareProperty(StringArrayProperty(name='InputFiles'), + doc='Comma separated list of input files') + + self.declareProperty(name='SumFiles', defaultValue=False, doc='Toggle input file summing or sequential processing') + + self.declareProperty(WorkspaceProperty('CalibrationWorkspace', '', + direction=Direction.Input, optional=PropertyMode.Optional), doc='Workspace contining calibration data') + + # Instrument configuration properties + self.declareProperty(name='Instrument', defaultValue='', doc='Instrument used during run.', + validator=StringListValidator(['IRIS', 'OSIRIS', 'TOSCA', 'TFXA'])) + self.declareProperty(name='Analyser', defaultValue='', doc='Analyser bank used during run.', + validator=StringListValidator(['graphite', 'mica', 'fmica'])) + self.declareProperty(name='Reflection', defaultValue='', doc='Reflection number for instrument setup during run.', + validator=StringListValidator(['002', '004', '006'])) + + self.declareProperty(IntArrayProperty(name='SpectraRange', values=[0, 1], + validator=IntArrayMandatoryValidator()), + doc='Comma separated range of spectra number to use.') + self.declareProperty(FloatArrayProperty(name='BackgroundRange'), + doc='Range of background to subtact from raw data in time of flight.') + self.declareProperty(name='RebinString', defaultValue='', doc='Rebin string parameters.') + self.declareProperty(name='DetailedBalance', defaultValue='', doc='') + self.declareProperty(name='ScaleFactor', defaultValue=1.0, doc='Factor by which to scale result.') + self.declareProperty(name='FoldMultipleFrames', defaultValue=True, + doc='Folds multiple framed data sets into a single workspace.') + + # Spectra grouping options + self.declareProperty(name='GroupingMethod', defaultValue='IPF', + validator=StringListValidator(['Individual', 'All', 'File', 'Workspace', 'IPF']), + doc='Method used to group spectra.') + self.declareProperty(WorkspaceProperty('GroupingWorkspace', '', + direction=Direction.Input, optional=PropertyMode.Optional), + doc='Workspace containing spectra grouping.') + self.declareProperty(FileProperty('MapFile', '', + action=FileAction.OptionalLoad, extensions=['.map']), + doc='Workspace containing spectra grouping.') + + # Output properties + self.declareProperty(name='UnitX', defaultValue='DeltaE', doc='X axis units for the result workspace.', + validator=StringListValidator(['DeltaE', 'DeltaE_inWavenumber'])) + self.declareProperty(StringArrayProperty(name='SaveFormats'), doc='Comma seperated list of save formats') + self.declareProperty(name='Plot', defaultValue='None', doc='Type of plot to output after reduction.', + validator=StringListValidator(['None', 'Spectra', 'Contour', 'Both'])) + + self.declareProperty(WorkspaceGroupProperty('OutputWorkspace', '', + direction=Direction.Output), + doc='Workspace group for the resulting workspaces.') + + + def PyExec(self): + self._setup() + self._load_files() + + for c_ws_name in self._workspace_names: + is_multi_frame = isinstance(mtd[c_ws_name], WorkspaceGroup) + + # Get list of workspaces + if is_multi_frame: + workspaces = mtd[c_ws_name].getNames() + else: + workspaces = [c_ws_name] + + # Process rebinning for framed data + if self._rebin_string is not None and is_multi_frame: + rebin_string_comp = self._rebin_string.split(',') + if len(rebin_string_comp) >= 5: + rebin_string_2 = ','.join(rebin_string_comp[2:]) + else: + rebin_string_2 = self._rebin_string + + bin_counts = [mtd[ws].blocksize() for ws in mtd[c_ws_name].getNames()] + num_bins = np.amax(bin_counts) + + masked_detectors = self._identify_bad_detectors(workspaces[0]) + + # Process workspaces + for ws_name in workspaces: + monitor_ws_name = ws_name + '_mon' + + # Process monitor + if not self._unwrap_monitor(ws_name): + ConvertUnits(InputWorkspace=monitor_ws_name, OutputWorkspace=monitor_ws_name, Target='Wavelength', EMode='Elastic') + + self._process_monitor_efficiency(ws_name) + self._scale_monitor(ws_name) + + + # Do background removal if a range was provided + if self._background_range is not None: + ConvertToDistribution(Workspace=ws_name) + CalculateFlatBackground(InputWorkspace=ws_name, OutputWorkspace=ws_name, + StartX=self._background_range[0], + EndX=self._background_range[1], + Mode='Mean') + ConvertFromDistribution(Workspace=ws_name) + + # Divide by the calibration workspace if one was provided + if self._calibration_ws is not None: + Divide(LHSWorkspace=ws_name, + RHSWorkspace=self._calibration_ws, + OutputWorkspace=ws_name) + + # Scale detector data by monitor intensities + ConvertUnits(InputWorkspace=ws_name, OutputWorkspace=ws_name, Target='Wavelength', EMode='Indirect') + RebinToWorkspace(WorkspaceToRebin=ws_name, WorkspaceToMatch=monitor_ws_name, OutputWorkspace=ws_name) + Divide(LHSWorkspace=ws_name, RHSWorkspace=monitor_ws_name, OutputWorkspace=ws_name) + + # Remove the no longer needed monitor workspace + DeleteWorkspace(monitor_ws_name) + + # Convert to energy + ConvertUnits(InputWorkspace=ws_name, OutputWorkspace=ws_name, Target='DeltaE', EMode='Indirect') + CorrectKiKf(InputWorkspace=ws_name, OutputWorkspace=ws_name, EMode='Indirect') + + # Handle rebinning + if self._rebin_string is not None: + if is_multi_frame: + # Mulit frame data + if mtd[ws_name].blocksize() == num_bins: + Rebin(InputWorkspace=ws_name, OutputWorkspace=ws_name, Params=self._rebin_string) + else: + Rebin(InputWorkspace=ws_name, OutputWorkspace=ws_name, Params=rebin_string_2) + else: + # Regular data + Rebin(InputWorkspace=ws_name, OutputWorkspace=ws_name, Params=self._rebin_string) + else: + try: + # If user does not want to rebin then just ensure uniform binning across spectra + RebinToWorkspace(WorkspaceToRebin=ws_name, WorkspaceToMatch=ws_name, OutputWorkspace=ws_name) + except RuntimeError: + logger.warning('Rebinning failed, will try to continue anyway.') + + # Detailed balance + if self._detailed_balance is not None: + corr_factor = 11.606 / (2 * self._detailed_balance) + ExponentialCorrection(InputWorkspaces=ws_name, OutputWorkspace=ws_name, + C0=1.0, C1=corr_factor, Operation='Multiply') + + # Scale + if self._scale_factor != 1.0: + Scale(InputWorkspaces=ws_name, OutputWorkspace=ws_name, + Factor=self._scale_factor, Operation='Multiply') + + # Group spectra + self._group_spectra(ws_name, masked_detectors) + + if self._fold_multiple_frames and is_multi_frame: + self._fold_chopped(c_ws_name) + + # Convert to output units if needed + if self._output_x_units != 'DeltaE': + ConvertUnits(InputWorkspace=c_ws_name, OutputWorkspace=c_ws_name, + EMode='Indirect', Target=self._output_x_units) + + # Rename output workspaces + output_workspace_names = [self._rename_workspace(ws_name) for ws_name in self._workspace_names] + + # Save result workspaces + if self._save_formats is not None: + self._save(output_workspace_names) + + # Group result workspaces + GroupWorkspaces(InputWorkspaces=output_workspace_names, OutputWorkspace=self._output_ws) + + self.setProperty('OutputWorkspace', self._output_ws) + + # Plot result workspaces + if self._plot_type != 'None': + for ws_name in mtd[self._output_ws].getNames(): + self._plot_workspace(ws_name) + + + def validateInputs(self): + """ + Validates algorithm properties. + """ + issues = dict() + + # Validate the instrument configuration by checking if a parameter file exists + instrument_name = self.getPropertyValue('Instrument') + analyser = self.getPropertyValue('Analyser') + reflection = self.getPropertyValue('Reflection') + + ipf_filename = os.path.join(config['instrumentDefinition.directory'], + instrument_name + '_' + analyser + '_' + reflection + '_Parameters.xml') + + if not os.path.exists(ipf_filename): + error_message = 'Invalid instrument configuration' + issues['Instrument'] = error_message + issues['Analyser'] = error_message + issues['Reflection'] = error_message + + # Validate spectra range + spectra_range = self.getProperty('SpectraRange').value + if len(spectra_range) != 2: + issues['SpectraRange'] = 'Range must contain exactly two items' + elif spectra_range[0] > spectra_range[1]: + issues['SpectraRange'] = 'Range must be in format: lower,upper' + + # Validate background range + background_range = _elems_or_none(self.getProperty('BackgroundRange').value) + if background_range is not None: + if len(background_range) != 2: + issues['BackgroundRange'] = 'Range must contain exactly two items' + elif background_range[0] > background_range[1]: + issues['BackgroundRange'] = 'Range must be in format: lower,upper' + + # Validate grouping method + grouping_method = self.getPropertyValue('GroupingMethod') + grouping_ws = _str_or_none(self.getPropertyValue('GroupingWorkspace')) + + if grouping_method == 'Workspace' and grouping_ws is None: + issues['GroupingWorkspace'] = 'Must select a grouping workspace for current GroupingWorkspace' + + # Validate save formats + save_formats = self.getProperty('SaveFormats').value + valid_formats = ['nxs', 'ascii', 'spe', 'nxspe', 'aclimax', 'davegrp'] + for format_name in save_formats: + if format_name not in valid_formats: + issues['SaveFormats'] = '%s is not a valid save format' % format_name + break + + return issues + + + def _setup(self): + """ + Gets algorithm properties. + """ + + # Get properties + self._data_files = self.getProperty('InputFiles').value + self._sum_files = self.getProperty('SumFiles').value + self._calibration_ws = _str_or_none(self.getPropertyValue('CalibrationWorkspace')) + + self._instrument_name = self.getPropertyValue('Instrument') + self._analyser = self.getPropertyValue('Analyser') + self._reflection = self.getPropertyValue('Reflection') + + self._spectra_range = self.getProperty('SpectraRange').value + self._background_range = _elems_or_none(self.getProperty('BackgroundRange').value) + self._rebin_string = _str_or_none(self.getPropertyValue('RebinString')) + self._detailed_balance = _float_or_none(self.getPropertyValue('DetailedBalance')) + self._scale_factor = self.getProperty('ScaleFactor').value + self._fold_multiple_frames = self.getProperty('FoldMultipleFrames').value + + self._grouping_method = self.getPropertyValue('GroupingMethod') + self._grouping_ws = _str_or_none(self.getPropertyValue('GroupingWorkspace')) + self._grouping_map_file = _str_or_none(self.getPropertyValue('MapFile')) + + self._output_x_units = self.getPropertyValue('UnitX') + self._plot_type = self.getPropertyValue('Plot') + self._save_formats = _elems_or_none(self.getProperty('SaveFormats').value) + + self._output_ws = self.getPropertyValue('OutputWorkspace') + + # Disable sum files if there is only one file + if len(self._data_files) == 1: + if self._sum_files: + logger.warning('SumFiles disabled when only one input file is provided.') + self._sum_files = False + + # Get the IPF filename + self._ipf_filename = os.path.join(config['instrumentDefinition.directory'], + self._instrument_name + '_' + self._analyser + '_' + self._reflection + '_Parameters.xml') + logger.information('Instrument parameter file: %s' % self._ipf_filename) + + # Warn when grouping options are to be ignored + if self._grouping_method != 'Workspace' and self._grouping_ws is not None: + logger.warning('GroupingWorkspace will be ignored by selected GroupingMethod') + + if self._grouping_method != 'File' and self._grouping_map_file is not None: + logger.warning('MapFile will be ignored by selected GroupingMethod') + + # The list of workspaces being processed + self._workspace_names = [] + + + def _load_files(self): + """ + Loads a set of files and extracts just the spectra we care about (i.e. detector range and monitor). + """ + + for filename in self._data_files: + # The filename without path and extension will be the workspace name + ws_name = os.path.splitext(os.path.basename(filename))[0] + logger.debug('Loading file %s as workspace %s' % (filename, ws_name)) + + Load(Filename=filename, OutputWorkspace=ws_name) + + # Load the instrument parameters + LoadParameterFile(Workspace=ws_name, Filename=self._ipf_filename) + + # Add the workspace to the list of workspaces + self._workspace_names.append(ws_name) + + # Get the spectrum number for the monitor + instrument = mtd[ws_name].getInstrument() + monitor_index = int(instrument.getNumberParameter('Workflow.Monitor1-SpectrumNumber')[0]) + logger.debug('Workspace %s monitor 1 spectrum number :%d' % (ws_name, monitor_index)) + + # Chop data if required + try: + chop_threshold = mtd[ws_name].getInstrument().getNumberParameter('Workflow.ChopDataIfGreaterThan')[0] + x_max = mtd[ws_name].readX(0)[-1] + self._chopped_data = x_max > chop_threshold + except IndexError: + self._chopped_data = False + logger.information('Workspace %s need data chop: %s' % (ws_name, str(self._chopped_data))) + + workspaces = [ws_name] + if self._chopped_data: + ChopData(InputWorkspace=ws_name, OutputWorkspace=ws_name, MonitorWorkspaceIndex=monitor_index, + IntegrationRangeLower=5000.0, IntegrationRangeUpper=10000.0, NChops=5) + workspaces = mtd[ws_name].getNames() + + for chop_ws_name in workspaces: + # Get the monitor spectrum + monitor_ws_name = chop_ws_name + '_mon' + ExtractSingleSpectrum(InputWorkspace=chop_ws_name, OutputWorkspace=monitor_ws_name, + WorkspaceIndex=monitor_index) + + # Crop to the detectors required + CropWorkspace(InputWorkspace=chop_ws_name, OutputWorkspace=chop_ws_name, + StartWorkspaceIndex=int(self._spectra_range[0]) - 1, + EndWorkspaceIndex=int(self._spectra_range[1]) - 1) + + logger.information('Loaded workspace names: %s' % (str(self._workspace_names))) + logger.information('Chopped data: %s' % (str(self._chopped_data))) + + # Sum files if needed + if self._sum_files: + if self._chopped_data: + self._sum_chopped_runs() + else: + self._sum_regular_runs() + + logger.information('Summed workspace names: %s' % (str(self._workspace_names))) + + + def _sum_regular_runs(self): + """ + Sum runs with single workspace data. + """ + + # Use the first workspace name as the result of summation + summed_detector_ws_name = self._workspace_names[0] + summed_monitor_ws_name = self._workspace_names[0] + '_mon' + + # Get a list of the run numbers for the original data + run_numbers = ','.join([str(mtd[ws_name].getRunNumber()) for ws_name in self._workspace_names]) + + # Generate lists of the detector and monitor workspaces + detector_workspaces = ','.join(self._workspace_names) + monitor_workspaces = ','.join([ws_name + '_mon' for ws_name in self._workspace_names]) + + # Merge the raw workspaces + MergeRuns(InputWorkspaces=detector_workspaces, OutputWorkspace=summed_detector_ws_name) + MergeRuns(InputWorkspaces=monitor_workspaces, OutputWorkspace=summed_monitor_ws_name) + + # Delete old workspaces + for idx in range(1, len(self._workspace_names)): + DeleteWorkspace(self._workspace_names[idx]) + DeleteWorkspace(self._workspace_names[idx] + '_mon') + + # Derive the scale factor based on number of merged workspaces + scale_factor = 1.0 / len(self._workspace_names) + logger.information('Scale factor for summed workspaces: %f' % scale_factor) + + # Scale the new detector and monitor workspaces + Scale(InputWorkspace=summed_detector_ws_name, OutputWorkspace=summed_detector_ws_name, + Factor=scale_factor) + Scale(InputWorkspace=summed_monitor_ws_name, OutputWorkspace=summed_monitor_ws_name, + Factor=scale_factor) + + # Add the list of run numbers to the result workspace as a sample log + AddSampleLog(Workspace=summed_detector_ws_name, LogName='multi_run_numbers', + LogType='String', LogText=run_numbers) + + # Only have the one workspace now + self._workspace_names = [summed_detector_ws_name] + + + def _sum_chopped_runs(self): + """ + Sum runs with chopped data. + """ + + try: + num_merges = len(mtd[self._workspace_names[0]].getNames()) + except: + raise RuntimeError('Not all runs have been chapped, cannot sum.') + + merges = list() + + # Generate a list of workspaces to be merged + for idx in range(0, num_merges): + merges.append({'detector':list(), 'monitor':list()}) + + for ws_name in self._workspace_names: + detector_ws_name = mtd[ws_name].getNames()[idx] + monitor_ws_name = detector_ws_name + '_mon' + + merges[idx]['detector'].append(detector_ws_name) + merges[idx]['monitor'].append(monitor_ws_name) + + for merge in merges: + # Merge the chopped run segments + MergeRuns(InputWorkspaces=','.join(merge['detector']), OutputWorkspace=merge['detector'][0]) + MergeRuns(InputWorkspaces=','.join(merge['monitor']), OutputWorkspace=merge['monitor'][0]) + + # Scale the merged runs + merge_size = len(merge['detector']) + factor = 1.0 / merge_size + Scale(InputWorkspace=merge['detector'][0], OutputWorkspace=merge['detector'][0], Factor=factor, Operation='Multiply') + Scale(InputWorkspace=merge['monitor'][0], OutputWorkspace=merge['monitor'][0], Factor=factor, Operation='Multiply') + + # Remove the old workspaces + for idx in range(1, merge_size): + DeleteWorkspace(merge['detector'][idx]) + DeleteWorkspace(merge['monitor'][idx]) + + # Only have the one workspace now + self._workspace_names = [self._workspace_names[0]] + + + def _identify_bad_detectors(self, ws_name): + """ + Identify detectors which should be masked + + @param ws_name Name of worksapce to use ot get masking detectors + """ + + instrument = mtd[ws_name].getInstrument() + + try: + masking_type = instrument.getStringParameter('Workflow.Masking')[0] + except IndexError: + masking_type = 'None' + + logger.information('Masking type: %s' % (masking_type)) + + masked_spec = list() + + if masking_type == 'IdentifyNoisyDetectors': + ws_mask = '__workspace_mask' + IdentifyNoisyDetectors(InputWorkspace=ws_name, OutputWorkspace=ws_mask) + + # Convert workspace to a list of spectra + num_spec = mtd[ws_mask].getNumberHistograms() + masked_spec = [spec for spec in range(0, num_spec) if mtd[ws_mask].readY(spec)[0] == 0.0] + + # Remove the temporary masking workspace + DeleteWorkspace(ws_mask) + + logger.debug('Masked specta for workspace %s: %s' % (ws_name, str(masked_spec))) + + return masked_spec + + + def _unwrap_monitor(self, ws_name): + """ + Unwrap monitor if required based on value of Workflow.UnwrapMonitor parameter + + @param ws_name Name of workspace + @return True if the monitor was unwrapped + """ + + monitor_ws_name = ws_name + '_mon' + instrument = mtd[monitor_ws_name].getInstrument() + + # Determine if the monitor should be unwrapped + try: + unwrap = instrument.getStringParameter('Workflow.UnwrapMonitor')[0] + + if unwrap == 'Always': + should_unwrap = True + elif unwrap == 'BaseOnTimeRegime': + mon_time = mtd[monitor_ws_name].readX(0)[0] + det_time = mtd[ws_name].readX(0)[0] + logger.notice(str(mon_time) + " " + str(det_time)) + should_unwrap = mon_time == det_time + else: + should_unwrap = False + + except IndexError: + should_unwrap = False + + logger.debug('Need to unwrap monitor for %s: %s' % (ws_name, str(should_unwrap))) + + if should_unwrap: + sample = instrument.getSample() + sample_to_source = sample.getPos() - instrument.getSource().getPos() + radius = mtd[ws_name].getDetector(0).getDistance(sample) + z_dist = sample_to_source.getZ() + l_ref = z_dist + radius + + logger.debug('For workspace %s: radius=%d, z_dist=%d, l_ref=%d' % + (ws_name, radius, z_dist, l_ref)) + + _, join = UnwrapMonitor(InputWorkspace=monitor_ws_name, + OutputWorkspace=monitor_ws_name, LRef=l_ref) + + RemoveBins(InputWorkspace=monitor_ws_name, OutputWorkspace=monitor_ws_name, + XMin=join - 0.001, XMax=join + 0.001, + Interpolation='Linear') + + try: + FFTSmooth(InputWorkspace=monitor_ws_name, OutputWorkspace=monitor_ws_name, WorkspaceIndex=0) + except ValueError: + raise ValueError('Uneven bin widths are not supported.') + + return should_unwrap + + + def _process_monitor_efficiency(self, ws_name): + """ + Process monitor efficiency for a given workspace. + + @param ws_name Name of workspace to process monitor for + """ + + monitor_ws_name = ws_name + '_mon' + instrument = mtd[ws_name].getInstrument() + + try: + area = instrument.getNumberParameter('Workflow.Monitor1-Area')[0] + thickness = instrument.getNumberParameter('Workflow.Monitor1-Thickness')[0] + attenuation = instrument.getNumberParameter('Workflow.Monitor1-Attenuation')[0] + except IndexError: + raise ValueError('Cannot get monitor details form parameter file') + + if area == -1 or thickness == -1 or attenuation == -1: + logger.information('For workspace %s, skipping monitor efficiency' % (ws_name)) + return + + OneMinusExponentialCor(InputWorkspace=monitor_ws_name, OutputWorkspace=monitor_ws_name, + C=attenuation * thickness, C1=area) + + + def _scale_monitor(self, ws_name): + """ + Scale monitor intensity by a factor given as the Workflow.MonitorScalingFactor parameter. + + @param ws_name Name of workspace to process monitor for + """ + + monitor_ws_name = ws_name + '_mon' + instrument = mtd[ws_name].getInstrument() + + try: + scale_factor = instrument.getNumberParameter('Workflow.Monitor1-ScalingFactor')[0] + except IndexError: + logger.information('No monitor scaling factor found for workspace %s' % ws_name) + return + + if scale_factor != 1.0: + Scale(InputWorkspace=monitor_ws_name, OutputWorkspace=monitor_ws_name, + Factor=1.0 / scale_factor, Operation='Multiply') + + + def _group_spectra(self, ws_name, masked_detectors): + """ + Groups spectra in a given workspace according to the Workflow.GroupingMethod and + Workflow.GroupingFile parameters and GrpupingPolicy property. + + @param ws_name Name of workspace to group spectra of + @param masked_detectors List of spectra numbers to mask + """ + + instrument = mtd[ws_name].getInstrument() + + # If grouping as per he IPF is desired + if self._grouping_method == 'IPF': + # Get the grouping method from the parameter file + try: + grouping_method = instrument.getStringParameter('Workflow.GroupingMethod')[0] + except IndexError: + grouping_method = 'Individual' + + else: + # Otherwise use the value of GroupingPolicy + grouping_method = self._grouping_method + + logger.information('Grouping method for workspace %s is %s' % (ws_name, grouping_method)) + + if grouping_method == 'Individual': + # Nothing to do here + return + + elif grouping_method == 'All': + # Get a list of all spectra minus those which are masked + num_spec = mtd[ws_name].getNumberHistograms() + spectra_list = [spec for spec in range(0, num_spec) if spec not in masked_detectors] + + # Apply the grouping + GroupDetectors(InputWorkspace=ws_name, OutputWorkspace=ws_name, Behaviour='Average', + WorkspaceIndexList=spectra_list) + + elif grouping_method == 'File': + # Get the filename for the grouping file + if self._grouping_map_file is not None: + grouping_file = self._grouping_map_file + else: + try: + grouping_file = instrument.getStringParameter('Workflow.GroupingFile')[0] + except IndexError: + raise RuntimeError('Cannot get grouping file from properties or IPF.') + + # If the file is not found assume it is in the grouping files directory + if not os.path.isfile(grouping_file): + grouping_file = os.path.join(config.getString('groupingFiles.directory'), grouping_file) + + # If it is still not found just give up + if not os.path.isfile(grouping_file): + raise RuntimeError('Cannot find grouping file: %s' % (grouping_file)) + + # Mask detectors if required + if len(masked_detectors) > 0: + MaskDetectors(Workspace=ws_name, WorkspaceIndexList=masked_detectors) + + # Apply the grouping + GroupDetectors(InputWorkspace=ws_name, OutputWorkspace=ws_name, Behaviour='Average', + MapFile=grouping_file) + + elif grouping_method == 'Workspace': + # Apply the grouping + GroupDetectors(InputWorkspace=ws_name, OutputWorkspace=ws_name, Behaviour='Average', + CopyGroupingFromWorkspace=self._grouping_ws) + + else: + raise RuntimeError('Invalid grouping method %s for workspace %s' % (grouping_method, ws_name)) + + + def _fold_chopped(self, ws_name): + """ + Folds multiple frames of a data set into one workspace. + + @param ws_name Name of the group to fold + """ + + workspaces = mtd[ws_name].getNames() + merged_ws = ws_name + '_merged' + MergeRuns(InputWorkspaces=','.join(workspaces), OutputWorkspace=merged_ws) + + scaling_ws = '__scaling_ws' + unit = mtd[ws_name].getItem(0).getAxis(0).getUnit().unitID() + + ranges = [] + for ws in mtd[ws_name].getNames(): + x_min = mtd[ws].dataX(0)[0] + x_max = mtd[ws].dataX(0)[-1] + ranges.append((x_min, x_max)) + DeleteWorkspace(Workspace=ws) + + data_x = mtd[merged_ws].readX(0) + data_y = [] + data_e = [] + + for i in range(0, mtd[merged_ws].blocksize()): + y_val = 0.0 + for rng in ranges: + if data_x[i] >= rng[0] and data_x[i] <= rng[1]: + y_val += 1.0 + + data_y.append(y_val) + data_e.append(0.0) + + CreateWorkspace(OutputWorkspace=scaling_ws, DataX=data_x, DataY=data_y, DataE=data_e, UnitX=unit) + + Divide(LHSWorkspace=merged_ws, RHSWorkspace=scaling_ws, OutputWorkspace=ws_name) + DeleteWorkspace(Workspace=merged_ws) + DeleteWorkspace(Workspace=scaling_ws) + + def _rename_workspace(self, ws_name): + """ + Renames a worksapce according to the naming policy in the Workflow.NamingConvention parameter. + + @param ws_name Name of workspace + @return New name of workspace + """ + + is_multi_frame = isinstance(mtd[ws_name], WorkspaceGroup) + + # Get the instrument + if is_multi_frame: + instrument = mtd[ws_name].getItem(0).getInstrument() + else: + instrument = mtd[ws_name].getInstrument() + + # Get the naming convention parameter form the parameter file + try: + convention = instrument.getStringParameter('Workflow.NamingConvention')[0] + except IndexError: + # Defualt to run title if naming convention parameter not set + convention = 'RunTitle' + logger.information('Naming convention for workspace %s is %s' % (ws_name, convention)) + + # Get run number + if is_multi_frame: + run_number = mtd[ws_name].getItem(0).getRun()['run_number'].value + else: + run_number = mtd[ws_name].getRun()['run_number'].value + logger.information('Run number for workspace %s is %s' % (ws_name, run_number)) + + inst_name = instrument.getName() + for facility in config.getFacilities(): + try: + short_inst_name = facility.instrument(inst_name).shortName() + break + except: + pass + logger.information('Short name for instrument %s is %s' % (inst_name, short_inst_name)) + + # Get run title + if is_multi_frame: + run_title = mtd[ws_name].getItem(0).getRun()['run_title'].value.strip() + else: + run_title = mtd[ws_name].getRun()['run_title'].value.strip() + logger.information('Run title for workspace %s is %s' % (ws_name, run_title)) + + if self._sum_files: + multi_run_marker = '_multi' + else: + multi_run_marker = '' + + if convention == 'None': + new_name = ws_name + + elif convention == 'RunTitle': + valid = "-_.() %s%s" % (string.ascii_letters, string.digits) + formatted_title = ''.join([c for c in run_title if c in valid]) + new_name = '%s%s%s-%s' % (short_inst_name.lower(), run_number, multi_run_marker, formatted_title) + + elif convention == 'AnalyserReflection': + analyser = instrument.getStringParameter('analyser')[0] + reflection = instrument.getStringParameter('reflection')[0] + new_name = '%s%s%s_%s%s_red' % (short_inst_name.upper(), run_number, multi_run_marker, + analyser, reflection) + + else: + raise RuntimeError('No valid naming convention for workspace %s' % ws_name) + + logger.information('New name for %s workspace: %s' % (ws_name, new_name)) + + RenameWorkspace(InputWorkspace=ws_name, OutputWorkspace=new_name) + return new_name + + + def _plot_workspace(self, ws_name): + """ + Plot a given workspace based on the Plot property. + + @param ws_name Name of workspace to plot + """ + + if self._plot_type == 'Spectra' or self._plot_type == 'Both': + from mantidplot import plotSpectrum + num_spectra = mtd[ws_name].getNumberHistograms() + try: + plotSpectrum(ws_name, range(0, num_spectra)) + except RuntimeError: + logger.notice('Spectrum plotting canceled by user') + + can_plot_contour = mtd[ws_name].getNumberHistograms() > 1 + if (self._plot_type == 'Contour' or self._plot_type == 'Both') and can_plot_contour: + from mantidplot import importMatrixWorkspace + plot_workspace = importMatrixWorkspace(ws_name) + plot_workspace.plotGraph2D() + + + def _save(self, worksspace_names): + """ + Saves the workspaces to the default save directory. + + @param worksspace_names List of workspace names to save + """ + + for ws_name in worksspace_names: + if 'spe' in self._save_formats: + SaveSPE(InputWorkspace=ws_name, Filename=ws_name + '.spe') + + if 'nxs' in self._save_formats: + SaveNexusProcessed(InputWorkspace=ws_name, Filename=ws_name + '.nxs') + + if 'nxspe' in self._save_formats: + SaveNXSPE(InputWorkspace=ws_name, Filename=ws_name + '.nxspe') + + if 'ascii' in self._save_formats: + # Version 1 of SaveASCII produces output that works better with excel/origin + # For some reason this has to be done with an algorithm object, using the function + # wrapper with Version did not change the version that was run + saveAsciiAlg = mantid.api.AlgorithmManager.createUnmanaged('SaveAscii', 1) + saveAsciiAlg.initialize() + saveAsciiAlg.setProperty('InputWorkspace', ws_name) + saveAsciiAlg.setProperty('Filename', ws_name + '.dat') + saveAsciiAlg.execute() + + if 'aclimax' in self._save_formats: + if self._output_x_units == 'DeltaE_inWavenumber': + bins = '24, -0.005, 4000' #cm-1 + else: + bins = '3, -0.005, 500' #meV + Rebin(InputWorkspace=ws_name,OutputWorkspace= ws_name + '_aclimax_save_temp', Params=bins) + SaveAscii(InputWorkspace=ws_name + '_aclimax_save_temp', Filename=ws_name + '_aclimax.dat', Separator='Tab') + DeleteWorkspace(Workspace=ws_name + '_aclimax_save_temp') + + if 'davegrp' in self._save_formats: + ConvertSpectrumAxis(InputWorkspace=ws_name, OutputWorkspace=ws_name + '_davegrp_save_temp', Target='ElasticQ', EMode='Indirect') + SaveDaveGrp(InputWorkspace=ws_name + '_davegrp_save_temp', Filename=ws_name + '.grp') + DeleteWorkspace(Workspace=ws_name + '_davegrp_save_temp') + + +# Register algorithm with Mantid +AlgorithmFactory.subscribe(ISISIndirectEnergyTransfer) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectCalibration.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectCalibration.py index 316d12877b0f512c79da826ec4437c7e67c8c642..e0b2ddb9cf82e0e26278a0310464c70db097232f 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectCalibration.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectCalibration.py @@ -29,26 +29,28 @@ class IndirectCalibration(DataProcessorAlgorithm): self.declareProperty(StringArrayProperty(name='InputFiles'), doc='Comma separated list of input files') - self.declareProperty(WorkspaceProperty('OutputWorkspace', '',\ - direction=Direction.Output), - doc='Output workspace for calibration data') - self.declareProperty(IntArrayProperty(name='DetectorRange', values=[0, 1],\ validator=IntArrayMandatoryValidator()), - doc='Range of detectors') + doc='Range of detectors.') self.declareProperty(FloatArrayProperty(name='PeakRange', values=[0.0, 100.0],\ validator=FloatArrayMandatoryValidator()), - doc='') + doc='Time of flight range over the peak.') self.declareProperty(FloatArrayProperty(name='BackgroundRange', values=[0.0, 1000.0],\ validator=FloatArrayMandatoryValidator()), - doc='') + doc='Time of flight range over the background.') self.declareProperty(name='ScaleFactor', defaultValue=1.0, - doc='') + doc='Factor by which to scale the result.') + + self.declareProperty(name='Plot', defaultValue=False, + doc='Plot the calibration data as a spectra plot.') + + self.declareProperty(WorkspaceProperty('OutputWorkspace', '', + direction=Direction.Output), + doc='Output workspace for calibration data.') - self.declareProperty(name='Plot', defaultValue=False, doc='Plot the calibration data') def validateInputs(self): @@ -107,14 +109,25 @@ class IndirectCalibration(DataProcessorAlgorithm): else: calib_ws_name = runs[0] - CalculateFlatBackground(InputWorkspace=calib_ws_name, OutputWorkspace=calib_ws_name,\ - StartX=self._back_range[0], EndX=self._back_range[1], Mode='Mean') + CalculateFlatBackground(InputWorkspace=calib_ws_name, OutputWorkspace=calib_ws_name, + StartX=self._back_range[0], EndX=self._back_range[1], Mode='Mean') + + number_historgrams = mtd[calib_ws_name].getNumberHistograms() + ws_mask, num_zero_spectra = FindDetectorsOutsideLimits(InputWorkspace=calib_ws_name, OutputWorkspace='__temp_ws_mask') + DeleteWorkspace(ws_mask) + + Integration(InputWorkspace=calib_ws_name, OutputWorkspace=calib_ws_name, + RangeLower=self._peak_range[0], RangeUpper=self._peak_range[1]) + + temp_sum = SumSpectra(InputWorkspace=calib_ws_name, OutputWorkspace='__temp_sum') + total = temp_sum.readY(0)[0] + DeleteWorkspace(temp_sum) + + if self._intensity_scale is None: + self._intensity_scale = 1 / (total / (number_historgrams - num_zero_spectra)) - from inelastic_indirect_reduction_steps import NormaliseToUnityStep - ntu = NormaliseToUnityStep() - ntu.set_factor(self._intensity_scale) - ntu.set_peak_range(self._peak_range[0], self._peak_range[1]) - ntu.execute(None, calib_ws_name) + Scale(InputWorkspace=calib_ws_name, OutputWorkspace=calib_ws_name, + Factor=self._intensity_scale, Operation='Multiply') RenameWorkspace(InputWorkspace=calib_ws_name, OutputWorkspace=self._out_ws) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectResolution.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectResolution.py index 1e3ccff2bcb72750d5385da98525dce03f355f14..4e44f558bbffd3a82cbdae85be954ca21d09d833 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectResolution.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/IndirectResolution.py @@ -22,62 +22,59 @@ class IndirectResolution(DataProcessorAlgorithm): def category(self): return 'Workflow\\Inelastic;PythonAlgorithms;Inelastic' + def summary(self): - return 'Creates a resolution workspace' + return 'Creates a resolution workspace for an indirect inelastic instrument.' + def PyInit(self): self.declareProperty(StringArrayProperty(name='InputFiles'), doc='Comma seperated list if input files') - self.declareProperty(WorkspaceProperty('OutputWorkspace', '',\ - optional=PropertyMode.Optional,\ - direction=Direction.Output), - doc='Output resolution workspace (if left blank a name will ' - 'be gernerated automatically)') - self.declareProperty(name='Instrument', defaultValue='', validator=StringListValidator(['IRIS', 'OSIRIS', 'TOSCA']), - doc='Instrument used during run') + doc='Instrument used during run.') self.declareProperty(name='Analyser', defaultValue='', validator=StringListValidator(['graphite', 'mica', 'fmica']), - doc='Analyser used during run') + doc='Analyser used during run.') self.declareProperty(name='Reflection', defaultValue='', validator=StringListValidator(['002', '004', '006']), - doc='Reflection used during run') + doc='Reflection used during run.') self.declareProperty(IntArrayProperty(name='DetectorRange', values=[0, 1]), - doc='Range of detetcors to use in resolution calculation') + doc='Range of detetcors to use in resolution calculation.') self.declareProperty(FloatArrayProperty(name='BackgroundRange', values=[0.0, 0.0]), - doc='Energy range to use as background') + doc='Energy range to use as background.') + self.declareProperty(name='RebinParam', defaultValue='', doc='Rebinning parameters (min,width,max)') self.declareProperty(name='ScaleFactor', defaultValue=1.0, doc='Factor to scale resolution curve by') + self.declareProperty(name='Plot', defaultValue=False, doc='Plot resolution curve') self.declareProperty(name='Save', defaultValue=False, doc='Save resolution workspace as a Nexus file') + self.declareProperty(WorkspaceProperty('OutputWorkspace', '', + direction=Direction.Output), + doc='Output resolution workspace.') def PyExec(self): from IndirectCommon import getWSprefix - self._setup() - InelasticIndirectReduction(Instrument=self._instrument, + ISISIndirectEnergyTransfer(Instrument=self._instrument, Analyser=self._analyser, Reflection=self._reflection, - Grouping='All', + GroupingMethod='All', SumFiles=True, InputFiles=self._input_files, - DetectorRange=self._detector_range, - OutputWorkspace='__icon_ws_group') - - icon_ws = mtd['__icon_ws_group'].getItem(0).getName() + SpectraRange=self._detector_range, + OutputWorkspace='__et_ws_group') - if self._out_ws == "": - self._out_ws = getWSprefix(icon_ws) + 'res' + icon_ws = mtd['__et_ws_group'].getItem(0).getName() if self._scale_factor != 1.0: Scale(InputWorkspace=icon_ws, OutputWorkspace=icon_ws, Factor=self._scale_factor) diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/InelasticIndirectReduction.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/InelasticIndirectReduction.py deleted file mode 100644 index 39070394a695e4f5815bd9135966038d838d16d4..0000000000000000000000000000000000000000 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/InelasticIndirectReduction.py +++ /dev/null @@ -1,246 +0,0 @@ -#pylint: disable=no-init -from mantid.kernel import * -from mantid.api import * -from mantid.simpleapi import * - - -class InelasticIndirectReduction(DataProcessorAlgorithm): - - _out_ws_group = None - _data_files = None - _instrument = None - _analyser = None - _reflection = None - _param_file = None - _detector_range = None - _background_range = None - _calib_ws_name = None - _detailed_balance = None - _rebin_string = None - _scale_factor = None - _sum_files = None - _map_file = None - _save_formats = None - _plot_type = None - _use_calib_ws = None - _use_detailed_balance = None - _use_scale_factor = None - _plot_ws = None - - def category(self): - return 'Workflow\\Inelastic;PythonAlgorithms;Inelastic' - - - def summary(self): - return 'Runs a reduction for an inelastic indirect geometry instrument.' - - - def PyInit(self): - self.declareProperty(StringArrayProperty(name='InputFiles'), - doc='Comma separated list of input files') - - self.declareProperty(WorkspaceGroupProperty('OutputWorkspace', '',\ - direction=Direction.Output), - doc='Workspace group for the resulting workspaces') - - self.declareProperty(name='SumFiles', defaultValue=False, doc='Toggle input file summing or sequential processing') - self.declareProperty(name='LoadLogs', defaultValue=False, doc='Load sample logs from input files') - - self.declareProperty(name='Instrument', defaultValue='', doc='Instrument used during run', - validator=StringListValidator(['IRIS', 'OSIRIS', 'TOSCA', 'TFXA', 'BASIS', 'VISION'])) - self.declareProperty(name='Analyser', defaultValue='', doc='Analyser used during run', - validator=StringListValidator(['graphite', 'mica', 'fmica', 'silicon'])) - self.declareProperty(name='Reflection', defaultValue='', doc='Reflection used during run', - validator=StringListValidator(['002', '004', '006', '111'])) - - self.declareProperty(WorkspaceProperty('CalibrationWorkspace', '',\ - direction=Direction.Input, optional=PropertyMode.Optional), doc='Workspace contining calibration data') - - self.declareProperty(IntArrayProperty(name='DetectorRange', values=[0, 1],\ - validator=IntArrayMandatoryValidator()), - doc='Comma separated range of detectors to use') - self.declareProperty(FloatArrayProperty(name='BackgroundRange'), - doc='') - - self.declareProperty(name='RebinString', defaultValue='', doc='Rebin string parameters') - self.declareProperty(name='DetailedBalance', defaultValue=-1.0, doc='') - self.declareProperty(name='ScaleFactor', defaultValue=1.0, doc='') - self.declareProperty(name='Grouping', defaultValue='', - doc='Method used to group spectra, can be either: Individual, All, a .map filename or a group workspace name') - self.declareProperty(name='Fold', defaultValue=False, doc='') - self.declareProperty(name='SaveCM1', defaultValue=False, doc='') - self.declareProperty(StringArrayProperty(name='SaveFormats'), doc='Comma separated list of save formats') - - self.declareProperty(name='Plot', defaultValue='none', doc='Type of plot to output after reduction', - validator=StringListValidator(['none', 'spectra', 'contour'])) - - - def PyExec(self): - from mantid import config, logger - import inelastic_indirect_reducer - - self._setup() - - # Setup reducer - reducer = inelastic_indirect_reducer.IndirectReducer() - - reducer.set_rename(True) - - reducer.set_instrument_name(self._instrument) - reducer.set_parameter_file(self._param_file) - try: - reducer.set_output_path(config["defaultsave.directory"]) - except RuntimeError: - pass # Use default - - for data_file in self._data_files: - reducer.append_data_file(data_file) - - reducer.set_sum_files(self._sum_files) - - reducer.set_detector_range(int(self._detector_range[0]) - 1, int(self._detector_range[1]) - 1) - - self._use_calib_ws = self._calib_ws_name != '' - if self._use_calib_ws: - logger.information('Using calibration workspace: %s' % self._calib_ws_name) - reducer.set_calibration_workspace(self._calib_ws_name) - - if len(self._background_range) == 2: - logger.debug('Using background range: ' + str(self._background_range)) - reducer.set_background(float(self._background_range[0]), float(self._background_range[1])) - - # TODO: There should be a better way to do this - self._use_detailed_balance = self._detailed_balance != -1.0 - if self._use_detailed_balance: - logger.debug('Using detailed balance: ' + str(self._detailed_balance)) - reducer.set_detailed_balance(self._detailed_balance) - - if self._rebin_string != '': - logger.debug('Using rebin string: ' + self._rebin_string) - reducer.set_rebin_string(self._rebin_string) - - self._use_scale_factor = self._scale_factor != 1.0 - if self._use_scale_factor: - logger.debug('Using scale factor: ' + str(self._scale_factor)) - reducer.set_scale_factor(self._scale_factor) - - if self._map_file != '': - logger.debug('Using mapping file: ' + str(self._map_file)) - reducer.set_grouping_policy(self._map_file) - - reducer.set_fold_multiple_frames(self.getProperty('Fold').value) - reducer.set_save_to_cm_1(self.getProperty('SaveCM1').value) - reducer.set_save_formats(self._save_formats) - - # Do reduction and get result workspaces - reducer.reduce() - ws_list = reducer.get_result_workspaces() - - self._plot_ws = ws_list[0] - - if len(ws_list) < 1: - logger.error('Failed to complete reduction') - return - - # Add sample logs to output workspace(s) - for workspace in ws_list: - self._add_ws_logs(workspace) - - # Group output workspaces - GroupWorkspaces(InputWorkspaces=ws_list, OutputWorkspace=self._out_ws_group) - self.setProperty('OutputWorkspace', self._out_ws_group) - - # Do plotting - if self._plot_type != 'none': - self._plot() - - - def validateInputs(self): - """ - Validates algorithm properties. - """ - issues = dict() - - # Validate save format string - save_formats = self.getProperty('SaveFormats').value - valid_formats = ['nxs', 'spe', 'nxspe', 'ascii', 'aclimax', 'davegrp'] - invalid_formats = list() - for save_format in save_formats: - if save_format not in valid_formats: - invalid_formats.append(save_format) - if len(invalid_formats) > 0: - issues['SaveFormats'] = 'The following save formats are not valid: ' + ','.join(invalid_formats) - - return issues - - - def _setup(self): - """ - Gets and algorithm properties. - """ - - # Get parameter values - self._out_ws_group = self.getPropertyValue('OutputWorkspace') - self._data_files = self.getProperty('InputFiles').value - - self._instrument = self.getPropertyValue('Instrument') - self._analyser = self.getPropertyValue('Analyser') - self._reflection = self.getPropertyValue('Reflection') - - self._param_file = config['instrumentDefinition.directory'] + self._instrument + '_' + self._analyser + '_' + self._reflection + '_Parameters.xml' - - self._detector_range = self.getProperty('DetectorRange').value - self._background_range = self.getProperty('BackgroundRange').value - - self._calib_ws_name = self.getPropertyValue('CalibrationWorkspace') - - self._detailed_balance = self.getProperty('DetailedBalance').value - self._rebin_string = self.getPropertyValue('RebinString') - self._scale_factor = self.getProperty('ScaleFactor').value - self._sum_files = self.getProperty('SumFiles').value - - self._map_file = self.getPropertyValue('Grouping') - - self._save_formats = self.getProperty('SaveFormats').value - self._plot_type = self.getPropertyValue('Plot') - - - def _add_ws_logs(self, workspace_name): - """ - Adds sample logs to a given output workspace. - """ - - AddSampleLog(Workspace=workspace_name, LogName='use_calib_wokspace', LogType='String', LogText=str(self._use_calib_ws)) - if self._use_calib_ws: - AddSampleLog(Workspace=workspace_name, LogName='calib_workspace_name', LogType='String', LogText=str(self._calib_ws_name)) - - AddSampleLog(Workspace=workspace_name, LogName='use_detailed_balance', LogType='String', LogText=str(self._use_detailed_balance)) - if self._use_detailed_balance: - AddSampleLog(Workspace=workspace_name, LogName='detailed_balance', LogType='Number', LogText=str(self._detailed_balance)) - - AddSampleLog(Workspace=workspace_name, LogName='use_scale_factor', LogType='String', LogText=str(self._use_scale_factor)) - if self._use_scale_factor: - AddSampleLog(Workspace=workspace_name, LogName='scale_factor', LogType='Number', LogText=str(self._scale_factor)) - - - def _plot(self): - """ - Plots results. - """ - - if self._plot_type == 'spectra': - from mantidplot import plotSpectrum - num_spectra = mtd[self._plot_ws].getNumberHistograms() - try: - plotSpectrum(self._plot_ws, range(0, num_spectra)) - except RuntimeError: - logger.notice('Spectrum plotting canceled by user') - - if self._plot_type == 'contour': - from mantidplot import importMatrixWorkspace - plot_workspace = importMatrixWorkspace(self._plot_ws) - plot_workspace.plotGraph2D() - - -# Register algorithm with Mantid -AlgorithmFactory.subscribe(InelasticIndirectReduction) diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt index 77b67b9edc8f9142c083ae3d3004f4ffe577537f..ad9fe2847ba1728ad63fe03af6a915570823620b 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt @@ -24,9 +24,9 @@ set ( TEST_PY_FILES IndirectCylinderAbsorptionTest.py IndirectFlatPlateAbsorptionTest.py IndirectILLReductionTest.py - InelasticIndirectReductionTest.py IndirectTransmissionTest.py IndirectTransmissionMonitorTest.py + ISISIndirectEnergyTransferTest.py LoadDNSLegacyTest.py LoadFullprofFileTest.py LoadLiveDataTest.py diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py new file mode 100644 index 0000000000000000000000000000000000000000..04e4ef43c3a2a576e67062bcae5f426e4a1dbdbb --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/ISISIndirectEnergyTransferTest.py @@ -0,0 +1,56 @@ +import unittest +from mantid.simpleapi import * +from mantid.api import * + + +class ISISIndirectEnergyTransferTest(unittest.TestCase): + + def test_basic_reduction_completes(self): + """ + Sanity test to ensure the most basic reduction actually completes. + """ + + ws = ISISIndirectEnergyTransfer(InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53]) + + self.assertTrue(isinstance(ws, WorkspaceGroup), 'Result workspace should be a workspace group.') + self.assertEqual(ws.getNames()[0], 'IRS26176_graphite002_red') + + + def test_instrument_validation_failure(self): + """ + Tests that an invalid instrument configuration causes the validation to fail. + """ + + self.assertRaises(RuntimeError, + ISISIndirectEnergyTransfer, + OutputWorkspace='__ISISIndirectEnergyTransferTest_ws', + InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='006', + SpectraRange=[3, 53]) + + + def test_group_workspace_validation_failure(self): + """ + Tests that validation fails when Workspace is selected as the GroupingMethod + but no workspace is provided. + """ + + self.assertRaises(RuntimeError, + ISISIndirectEnergyTransfer, + OutputWorkspace='__ISISIndirectEnergyTransferTest_ws', + InputFiles=['IRS26176.RAW'], + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53], + GroupingMethod='Workspace') + + +if __name__ == '__main__': + unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/InelasticIndirectReductionTest.py b/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/InelasticIndirectReductionTest.py deleted file mode 100644 index ef51954f33eb75801cbdd116de8c1f1f37c8e997..0000000000000000000000000000000000000000 --- a/Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/InelasticIndirectReductionTest.py +++ /dev/null @@ -1,21 +0,0 @@ -import unittest -from mantid import mtd -from mantid.simpleapi import InelasticIndirectReduction - - -class InelasticIndirectReductionTest(unittest.TestCase): - - def test_basic(self): - InelasticIndirectReduction(InputFiles='IRS26176.RAW', - OutputWorkspace='IndirectReductions', - Instrument='IRIS', - Analyser='graphite', - Reflection='002', - DetectorRange=[3, 53]) - - reduction_workspace = mtd['IndirectReductions'].getItem(0) - self.assertEquals(reduction_workspace.getName(), 'irs26176_graphite002_red') - - -if __name__ == "__main__": - unittest.main() diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.h index 167b3f9c1e28be3ba9e6b52e011af4fc15cdfb55..d164fa8aa6aae3a65e4c873d541e6d5527df0039 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.h @@ -63,7 +63,7 @@ namespace CustomInterfaces private: Ui::ISISEnergyTransfer m_uiForm; - QString createMapFile(const QString& groupType); ///< create the mapping file with which to group results + QPair<QString, QString> createMapFile(const QString& groupType); ///< create the mapping file with which to group results std::vector<std::string> getSaveFormats(); ///< get a vector of save formats }; diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui index e53c1319081ead9934fe2ec4154761eff166696a..e74fc8a361fc8a66aa3c8ae0531bf4c1b9e8d002 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>611</width> - <height>670</height> + <width>617</width> + <height>755</height> </rect> </property> <property name="minimumSize"> @@ -25,89 +25,67 @@ <property name="title"> <string>Input</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <property name="spacing"> - <number>0</number> - </property> - <item> - <layout class="QHBoxLayout" name="loInput"> - <item> - <layout class="QVBoxLayout" name="loInputSources"> - <item> - <widget class="MantidQt::MantidWidgets::MWRunFiles" name="dsRunFiles" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>41</verstretch> - </sizepolicy> - </property> - <property name="label" stdset="0"> - <string>Run Files</string> - </property> - <property name="multipleFiles" stdset="0"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="MantidQt::MantidWidgets::DataSelector" name="dsCalibrationFile" native="true"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoLoad" stdset="0"> - <bool>true</bool> - </property> - <property name="showLoad" stdset="0"> - <bool>false</bool> - </property> - <property name="workspaceSuffixes" stdset="0"> - <string>_calib</string> - </property> - <property name="fileBrowserSuffixes" stdset="0"> - <string>_calib.nxs</string> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QVBoxLayout" name="loInputOptions"> - <item> - <widget class="QCheckBox" name="ckSumFiles"> - <property name="toolTip"> - <string>Sum multiple files together.</string> - </property> - <property name="text"> - <string>Sum Files</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="ckLoadLogs"> - <property name="text"> - <string>Load Logs</string> - </property> - </widget> - </item> - <item> - <widget class="QCheckBox" name="ckUseCalib"> - <property name="toolTip"> - <string>Use calibration file to adjust for detector efficiency.</string> - </property> - <property name="text"> - <string>Use Calib File</string> - </property> - </widget> - </item> - </layout> - </item> - </layout> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QCheckBox" name="ckSumFiles"> + <property name="toolTip"> + <string>Sum multiple files together.</string> + </property> + <property name="text"> + <string>Sum Files</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="MantidQt::MantidWidgets::DataSelector" name="dsCalibrationFile" native="true"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoLoad" stdset="0"> + <bool>true</bool> + </property> + <property name="showLoad" stdset="0"> + <bool>false</bool> + </property> + <property name="workspaceSuffixes" stdset="0"> + <string>_calib</string> + </property> + <property name="fileBrowserSuffixes" stdset="0"> + <string>_calib.nxs</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="ckUseCalib"> + <property name="toolTip"> + <string>Use calibration file to adjust for detector efficiency.</string> + </property> + <property name="text"> + <string>Use Calib File</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="MantidQt::MantidWidgets::MWRunFiles" name="dsRunFiles" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>41</verstretch> + </sizepolicy> + </property> + <property name="label" stdset="0"> + <string>Run Files</string> + </property> + <property name="multipleFiles" stdset="0"> + <bool>true</bool> + </property> + </widget> </item> </layout> </widget> @@ -256,9 +234,6 @@ <property name="sizeConstraint"> <enum>QLayout::SetDefaultConstraint</enum> </property> - <property name="spacing"> - <number>0</number> - </property> <item row="0" column="0"> <widget class="QCheckBox" name="ckDetailedBalance"> <property name="text"> @@ -336,9 +311,6 @@ <string>Conversion to Energy Transfer</string> </property> <layout class="QHBoxLayout" name="horizontalLayout"> - <property name="spacing"> - <number>6</number> - </property> <item> <widget class="QLabel" name="lbEFixed"> <property name="text"> @@ -422,12 +394,6 @@ <string>Rebin (meV)</string> </property> <layout class="QVBoxLayout" name="verticalLayout_14"> - <property name="spacing"> - <number>6</number> - </property> - <property name="margin"> - <number>6</number> - </property> <item> <layout class="QHBoxLayout" name="loRebin"> <property name="spacing"> @@ -822,18 +788,6 @@ <string>Output</string> </property> <layout class="QGridLayout" name="gridLayout_8"> - <property name="leftMargin"> - <number>6</number> - </property> - <property name="topMargin"> - <number>6</number> - </property> - <property name="rightMargin"> - <number>6</number> - </property> - <property name="horizontalSpacing"> - <number>0</number> - </property> <item row="1" column="0"> <layout class="QHBoxLayout" name="loSaveFormats"> <item> @@ -1126,25 +1080,25 @@ </hints> </connection> <connection> - <sender>ckUseCalib</sender> + <sender>ckBackgroundRemoval</sender> <signal>toggled(bool)</signal> - <receiver>dsCalibrationFile</receiver> + <receiver>spBackgroundStart</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> - <x>441</x> - <y>98</y> + <x>96</x> + <y>237</y> </hint> <hint type="destinationlabel"> - <x>159</x> - <y>86</y> + <x>476</x> + <y>238</y> </hint> </hints> </connection> <connection> <sender>ckBackgroundRemoval</sender> <signal>toggled(bool)</signal> - <receiver>spBackgroundStart</receiver> + <receiver>spBackgroundEnd</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> @@ -1152,24 +1106,24 @@ <y>237</y> </hint> <hint type="destinationlabel"> - <x>476</x> + <x>554</x> <y>238</y> </hint> </hints> </connection> <connection> - <sender>ckBackgroundRemoval</sender> + <sender>ckUseCalib</sender> <signal>toggled(bool)</signal> - <receiver>spBackgroundEnd</receiver> + <receiver>dsCalibrationFile</receiver> <slot>setEnabled(bool)</slot> <hints> <hint type="sourcelabel"> - <x>96</x> - <y>237</y> + <x>454</x> + <y>86</y> </hint> <hint type="destinationlabel"> - <x>554</x> - <y>238</y> + <x>162</x> + <y>86</y> </hint> </hints> </connection> diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISCalibration.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISCalibration.cpp index 11a8d425db0a29e383bbc6099e4cde2766e74a59..47f571cee9dcce3ca2b2355e12caf263d0feb348 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISCalibration.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISCalibration.cpp @@ -430,14 +430,14 @@ namespace CustomInterfaces QString detRange = QString::number(m_dblManager->value(m_properties["ResSpecMin"])) + "," + QString::number(m_dblManager->value(m_properties["ResSpecMax"])); - IAlgorithm_sptr reductionAlg = AlgorithmManager::Instance().create("InelasticIndirectReduction"); + IAlgorithm_sptr reductionAlg = AlgorithmManager::Instance().create("ISISIndirectEnergyTransfer"); reductionAlg->initialize(); reductionAlg->setProperty("Instrument", getInstrumentConfiguration()->getInstrumentName().toStdString()); reductionAlg->setProperty("Analyser", getInstrumentConfiguration()->getAnalyserName().toStdString()); reductionAlg->setProperty("Reflection", getInstrumentConfiguration()->getReflectionName().toStdString()); reductionAlg->setProperty("InputFiles", files.toStdString()); reductionAlg->setProperty("OutputWorkspace", "__IndirectCalibration_reduction"); - reductionAlg->setProperty("DetectorRange", detRange.toStdString()); + reductionAlg->setProperty("SpectraRange", detRange.toStdString()); reductionAlg->execute(); if(!reductionAlg->isExecuted()) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp index ae5c94bac4af7dc65a9fd3ccd8dda22f84ce187f..85286e02d02650a17b921f280d22bc2a971e8d12 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/Indirect/ISISEnergyTransfer.cpp @@ -1,13 +1,13 @@ #include "MantidQtCustomInterfaces/Indirect/ISISEnergyTransfer.h" #include "MantidGeometry/IDTypes.h" -#include "MantidQtCustomInterfaces/Background.h" #include "MantidQtCustomInterfaces/UserInputValidator.h" #include <QFileInfo> #include <QInputDialog> using namespace Mantid::API; +using MantidQt::API::BatchAlgorithmRunner; namespace MantidQt { @@ -106,10 +106,9 @@ namespace CustomInterfaces void ISISEnergyTransfer::run() { - using MantidQt::API::BatchAlgorithmRunner; - - IAlgorithm_sptr reductionAlg = AlgorithmManager::Instance().create("InelasticIndirectReduction", -1); + IAlgorithm_sptr reductionAlg = AlgorithmManager::Instance().create("ISISIndirectEnergyTransfer"); reductionAlg->initialize(); + BatchAlgorithmRunner::AlgorithmRuntimeProps reductionRuntimeProps; reductionAlg->setProperty("Instrument", getInstrumentConfiguration()->getInstrumentName().toStdString()); reductionAlg->setProperty("Analyser", getInstrumentConfiguration()->getAnalyserName().toStdString()); @@ -119,7 +118,6 @@ namespace CustomInterfaces reductionAlg->setProperty("InputFiles", files.toStdString()); reductionAlg->setProperty("SumFiles", m_uiForm.ckSumFiles->isChecked()); - reductionAlg->setProperty("LoadLogs", m_uiForm.ckLoadLogs->isChecked()); if(m_uiForm.ckUseCalib->isChecked()) { @@ -130,7 +128,7 @@ namespace CustomInterfaces std::vector<long> detectorRange; detectorRange.push_back(m_uiForm.spSpectraMin->value()); detectorRange.push_back(m_uiForm.spSpectraMax->value()); - reductionAlg->setProperty("DetectorRange", detectorRange); + reductionAlg->setProperty("SpectraRange", detectorRange); if(m_uiForm.ckBackgroundRemoval->isChecked()) { @@ -157,32 +155,23 @@ namespace CustomInterfaces if(m_uiForm.ckScaleMultiplier->isChecked()) reductionAlg->setProperty("ScaleFactor", m_uiForm.spScaleMultiplier->value()); - if(m_uiForm.cbGroupingOptions->currentText() != "Default") - { - QString grouping = createMapFile(m_uiForm.cbGroupingOptions->currentText()); - reductionAlg->setProperty("Grouping", grouping.toStdString()); - } + if(m_uiForm.ckCm1Units->isChecked()) + reductionAlg->setProperty("UnitX", "DeltaE_inWavenumber"); - reductionAlg->setProperty("Fold", m_uiForm.ckFold->isChecked()); - reductionAlg->setProperty("SaveCM1", m_uiForm.ckCm1Units->isChecked()); - reductionAlg->setProperty("SaveFormats", getSaveFormats()); + QPair<QString, QString> grouping = createMapFile(m_uiForm.cbGroupingOptions->currentText()); + reductionAlg->setProperty("GroupingMethod", grouping.first.toStdString()); - reductionAlg->setProperty("OutputWorkspace", "IndirectEnergyTransfer_Workspaces"); + if(grouping.first == "Workspace") + reductionRuntimeProps["GroupingWorkspace"] = grouping.second.toStdString(); + else if(grouping.second == "File") + reductionAlg->setProperty("MapFile", grouping.second.toStdString()); - // Plot Output options - switch(m_uiForm.cbPlotType->currentIndex()) - { - case 0: // "None" - break; - case 1: // "Spectra" - reductionAlg->setProperty("Plot", "spectra"); - break; - case 2: // "Contour" - reductionAlg->setProperty("Plot", "contour"); - break; - } + reductionAlg->setProperty("FoldMultipleFrames", m_uiForm.ckFold->isChecked()); + reductionAlg->setProperty("Plot", m_uiForm.cbPlotType->currentText().toStdString()); + reductionAlg->setProperty("SaveFormats", getSaveFormats()); + reductionAlg->setProperty("OutputWorkspace", "IndirectEnergyTransfer_Workspaces"); - m_batchAlgoRunner->addAlgorithm(reductionAlg); + m_batchAlgoRunner->addAlgorithm(reductionAlg, reductionRuntimeProps); connect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(algorithmComplete(bool))); disconnect(m_batchAlgoRunner, SIGNAL(batchComplete(bool)), this, SLOT(plotRawComplete(bool))); @@ -320,7 +309,7 @@ namespace CustomInterfaces * @param groupType :: Type of grouping (All, Group, Indiviual) * @return path to mapping file, or an empty string if file could not be created. */ - QString ISISEnergyTransfer::createMapFile(const QString& groupType) + QPair<QString, QString> ISISEnergyTransfer::createMapFile(const QString& groupType) { QString specRange = m_uiForm.spSpectraMin->text() + "," + m_uiForm.spSpectraMax->text(); @@ -328,10 +317,9 @@ namespace CustomInterfaces { QString groupFile = m_uiForm.dsMapFile->getFirstFilename(); if(groupFile == "") - { emit showMessageBox("You must enter a path to the .map file."); - } - return groupFile; + + return qMakePair(QString("File"), groupFile); } else if(groupType == "Groups") { @@ -347,18 +335,22 @@ namespace CustomInterfaces m_batchAlgoRunner->addAlgorithm(groupingAlg); - return groupWS; + return qMakePair(QString("Workspace"), groupWS); + } + else if (groupType == "Default") + { + return qMakePair(QString("IPF"), QString()); } else { // Catch All and Individual - return groupType; + return qMakePair(groupType, QString()); } } /** * Converts the checkbox selection to a comma delimited list of save formats for the - * InelasticIndirectReduction algorithm. + * ISISIndirectEnergyTransfer algorithm. * * @return A vector of save formats */ diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py index 38db816a22fb361f39ccdbf52fb9d3fbd645cc87..f920400b261a3a95578cc714c5ba5f9358dc1537 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/ISISIndirectInelastic.py @@ -10,7 +10,6 @@ from mantid.simpleapi import * from mantid.api import FileFinder # Import our workflows. -from inelastic_indirect_reducer import IndirectReducer from IndirectDataAnalysis import furyfitSeq, furyfitMult, confitSeq, abscorFeeder ''' @@ -18,11 +17,10 @@ from IndirectDataAnalysis import furyfitSeq, furyfitMult, confitSeq, abscorFeede - OSIRIS/IRIS supported by all tabs / interfaces. - VESUVIO is not supported by any interface as of yet. -For diagrams on the intended work flow of the IDA and Indirect parts of the -C2E interface, please see: +For diagrams on the intended work flow of the IDR and IDA interfaces see: -- http://www.mantidproject.org/IDA -- http://www.mantidproject.org/Indirect +- Indirect_DataReduction.rst +- Indirect_DataAnalysis.rst System test class hierarchy as shown below: @@ -80,7 +78,8 @@ stresstesting.MantidStressTest class ISISIndirectInelasticBase(stresstesting.MantidStressTest): - '''A common base class for the ISISIndirectInelastic* base classes. + ''' + A common base class for the ISISIndirectInelastic* base classes. ''' __metaclass__ = ABCMeta # Mark as an abstract class @@ -96,15 +95,18 @@ class ISISIndirectInelasticBase(stresstesting.MantidStressTest): raise NotImplementedError("Implement _run.") def validate_results_and_references(self): + num_ref_files = len(self.get_reference_files()) + num_results = len(self.result_names) + if type(self.get_reference_files()) != list: raise RuntimeError("The reference file(s) should be in a list") if type(self.result_names) != list: raise RuntimeError("The result workspace(s) should be in a list") - if len(self.get_reference_files()) !=\ - len(self.result_names): - raise RuntimeError("The number of result workspaces does not match" - " the number of reference files.") - if len(self.get_reference_files()) < 1: + if num_ref_files != num_results: + raise RuntimeError("The number of result workspaces (%d) does not match" + " the number of reference files (%d)." % ( + num_ref_files, num_results)) + if num_ref_files < 1 or num_results < 1: raise RuntimeError("There needs to be a least one result and " "reference.") @@ -147,8 +149,8 @@ class ISISIndirectInelasticBase(stresstesting.MantidStressTest): and returns the full path.''' return os.path.join(config['defaultsave.directory'], filename) - #============================================================================== + class ISISIndirectInelasticReduction(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic reduction tests @@ -171,23 +173,20 @@ class ISISIndirectInelasticReduction(ISISIndirectInelasticBase): '''Defines the workflow for the test''' self.tolerance = 1e-7 - reducer = IndirectReducer() - reducer.set_instrument_name(self.instr_name) - reducer.set_detector_range(self.detector_range[0], - self.detector_range[1]) - reducer.set_sum_files(self.sum_files) - self.parameter_file = self.instr_name + '_graphite_002_Parameters.xml' - reducer.set_parameter_file(self.parameter_file) - - for name in self.data_files: - reducer.append_data_file(name) + kwargs = {} if self.rebin_string is not None: - reducer.set_rebin_string(self.rebin_string) + kwargs['RebinString'] = self.rebin_string - # Do the reduction and rename the result. - reducer.reduce() - self.result_names = sorted(reducer.get_result_workspaces()) + reductions = ISISIndirectEnergyTransfer(Instrument=self.instr_name, + Analyser='graphite', + Reflection='002', + InputFiles=self.data_files, + SumFiles=self.sum_files, + SpectraRange=self.detector_range, + **kwargs) + + self.result_names = sorted(reductions.getNames()) def _validate_properties(self): '''Check the object properties are in an expected state to continue''' @@ -205,13 +204,12 @@ class ISISIndirectInelasticReduction(ISISIndirectInelasticBase): #------------------------- TOSCA tests ---------------------------------------- - class TOSCAReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'TOSCA' - self.detector_range = [0, 139] + self.detector_range = [1, 140] self.data_files = ['TSC15352.raw'] self.rebin_string = '-2.5,0.015,3,-0.005,1000' @@ -223,8 +221,8 @@ class TOSCAMultiFileReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'TOSCA' - self.detector_range = [0, 139] - self.data_files = ['TSC15352.raw', 'TSC15353.raw','TSC15354.raw'] + self.detector_range = [1, 140] + self.data_files = ['TSC15352.raw', 'TSC15353.raw', 'TSC15354.raw'] self.rebin_string = '-2.5,0.015,3,-0.005,1000' def get_reference_files(self): @@ -237,7 +235,7 @@ class TOSCAMultiFileSummedReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'TOSCA' - self.detector_range = [0, 139] + self.detector_range = [1, 140] self.data_files = ['TSC15352.raw', 'TSC15353.raw','TSC15354.raw'] self.rebin_string = '-2.5,0.015,3,-0.005,1000' self.sum_files = True @@ -245,16 +243,14 @@ class TOSCAMultiFileSummedReduction(ISISIndirectInelasticReduction): def get_reference_files(self): return ['II.TOSCAMultiFileSummedReduction.nxs'] - #------------------------- OSIRIS tests --------------------------------------- - class OSIRISReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'OSIRIS' - self.detector_range = [962, 1003] + self.detector_range = [963, 1004] self.data_files = ['OSIRIS00106550.raw'] self.rebin_string = None @@ -266,7 +262,7 @@ class OSIRISMultiFileReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'OSIRIS' - self.detector_range = [962, 1003] + self.detector_range = [963, 1004] self.data_files = ['OSIRIS00106550.raw',' OSIRIS00106551.raw'] self.rebin_string = None @@ -280,7 +276,7 @@ class OSIRISMultiFileSummedReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'OSIRIS' - self.detector_range = [962, 1003] + self.detector_range = [963, 1004] self.data_files = ['OSIRIS00106550.raw', 'OSIRIS00106551.raw'] self.rebin_string = None self.sum_files = True @@ -295,7 +291,7 @@ class IRISReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'IRIS' - self.detector_range = [2, 52] + self.detector_range = [3, 53] self.data_files = ['IRS21360.raw'] self.rebin_string = None @@ -308,7 +304,7 @@ class IRISMultiFileReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'IRIS' - self.detector_range = [2, 52] + self.detector_range = [3, 53] self.data_files = ['IRS21360.raw', 'IRS53664.raw'] self.rebin_string = None @@ -321,7 +317,7 @@ class IRISMultiFileSummedReduction(ISISIndirectInelasticReduction): def __init__(self): ISISIndirectInelasticReduction.__init__(self) self.instr_name = 'IRIS' - self.detector_range = [2, 52] + self.detector_range = [3, 53] self.data_files = ['IRS21360.raw', 'IRS53664.raw'] self.sum_files = True self.rebin_string = None @@ -331,19 +327,30 @@ class IRISMultiFileSummedReduction(ISISIndirectInelasticReduction): #as they should be the same return ['II.IRISMultiFileSummedReduction.nxs'] -#--------------------- Generic Reduction tests ----------------------------- +#============================================================================== class ISISIndirectInelasticReductionOutput(stresstesting.MantidStressTest): def runTest(self): - reducer = self._setup_reducer() - reducer.reduce() - self.result_names = sorted(reducer.get_result_workspaces()) + self.file_formats = ['nxs', 'spe', 'nxspe', 'ascii', 'aclimax'] + self.file_extensions = ['.nxs', '.spe', '.nxspe', '.dat', '_aclimax.dat'] - def validate(self): - self.assertEqual(len(self.result_names), 1) - self.result_name = self.result_names[0] + self.instr_name = 'TOSCA' + self.detector_range = [1, 140] + self.data_files = ['TSC15352.raw'] + self.rebin_string = '-2.5,0.015,3,-0.005,1000' + + reductions = ISISIndirectEnergyTransfer(Instrument=self.instr_name, + Analyser='graphite', + Reflection='002', + InputFiles=self.data_files, + SpectraRange=self.detector_range, + RebinString=self.rebin_string, + SaveFormats=self.file_formats) + + self.result_name = reductions[0].getName() + def validate(self): self.output_file_names = self._get_file_names() self.assert_reduction_output_exists(self.output_file_names) self.assert_ascii_file_matches() @@ -404,31 +411,6 @@ class ISISIndirectInelasticReductionOutput(stresstesting.MantidStressTest): actual_result = self._read_ascii_file(file_path, num_lines) self.assertTrue(actual_result == expected_result, msg + " (%s != %s)" % (actual_result, expected_result)) - def _setup_reducer(self): - self.file_formats = ['nxs', 'spe', 'nxspe', 'ascii', 'aclimax'] - self.file_extensions = ['.nxs', '.spe', '.nxspe', '.dat', '_aclimax.dat'] - self.instr_name = 'TOSCA' - self.detector_range = [0, 139] - self.data_files = ['TSC15352.raw'] - self.rebin_string = '-2.5,0.015,3,-0.005,1000' - self.parameter_file = self.instr_name + '_graphite_002_Parameters.xml' - - reducer = IndirectReducer() - reducer.set_instrument_name(self.instr_name) - reducer.set_detector_range(self.detector_range[0], - self.detector_range[1]) - reducer.set_sum_files(False) - reducer.set_parameter_file(self.parameter_file) - reducer.set_save_formats(self.file_formats) - - for name in self.data_files: - reducer.append_data_file(name) - - if self.rebin_string is not None: - reducer.set_rebin_string(self.rebin_string) - - return reducer - def _read_ascii_file(self, path, num_lines): with open(path,'rb') as file_handle: lines = [file_handle.readline().rstrip() for _ in xrange(num_lines)] @@ -446,6 +428,7 @@ class ISISIndirectInelasticReductionOutput(stresstesting.MantidStressTest): return output_names #============================================================================== + class ISISIndirectInelasticCalibration(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic calibration tests @@ -488,7 +471,6 @@ class ISISIndirectInelasticCalibration(ISISIndirectInelasticBase): #------------------------- OSIRIS tests --------------------------------------- - class OSIRISCalibration(ISISIndirectInelasticCalibration): def __init__(self): @@ -503,7 +485,6 @@ class OSIRISCalibration(ISISIndirectInelasticCalibration): #------------------------- IRIS tests --------------------------------------- - class IRISCalibration(ISISIndirectInelasticCalibration): def __init__(self): @@ -516,8 +497,8 @@ class IRISCalibration(ISISIndirectInelasticCalibration): def get_reference_files(self): return ["II.IRISCalibration.nxs"] - #============================================================================== + class ISISIndirectInelasticResolution(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic resolution tests @@ -572,7 +553,6 @@ class ISISIndirectInelasticResolution(ISISIndirectInelasticBase): #------------------------- OSIRIS tests --------------------------------------- - class OSIRISResolution(ISISIndirectInelasticResolution): def __init__(self): @@ -590,7 +570,6 @@ class OSIRISResolution(ISISIndirectInelasticResolution): #------------------------- IRIS tests ----------------------------------------- - class IRISResolution(ISISIndirectInelasticResolution): def __init__(self): @@ -606,8 +585,8 @@ class IRISResolution(ISISIndirectInelasticResolution): def get_reference_files(self): return ["II.IRISResolution.nxs"] - #============================================================================== + class ISISIndirectInelasticDiagnostics(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic diagnostic tests @@ -646,10 +625,8 @@ class ISISIndirectInelasticDiagnostics(ISISIndirectInelasticBase): if type(self.suffix) != str: raise RuntimeError("suffix property should be a string") - #------------------------- IRIS tests ----------------------------------------- - class IRISDiagnostics(ISISIndirectInelasticDiagnostics): def __init__(self): @@ -663,10 +640,8 @@ class IRISDiagnostics(ISISIndirectInelasticDiagnostics): def get_reference_files(self): return ["II.IRISDiagnostics.nxs"] - #------------------------- OSIRIS tests --------------------------------------- - class OSIRISDiagnostics(ISISIndirectInelasticDiagnostics): def __init__(self): @@ -680,8 +655,8 @@ class OSIRISDiagnostics(ISISIndirectInelasticDiagnostics): def get_reference_files(self): return ["II.OSIRISDiagnostics.nxs"] - #============================================================================== + class ISISIndirectInelasticMoments(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic TransformToIqt/TransformToIqtFit tests @@ -715,8 +690,8 @@ class ISISIndirectInelasticMoments(ISISIndirectInelasticBase): if type(self.scale) != float: raise RuntimeError("Scale should be a float") - #------------------------- OSIRIS tests --------------------------------------- + class OSIRISMoments(ISISIndirectInelasticMoments): def __init__(self): @@ -729,8 +704,8 @@ class OSIRISMoments(ISISIndirectInelasticMoments): def get_reference_files(self): return ['II.OSIRISMoments.nxs'] - #------------------------- IRIS tests ----------------------------------------- + class IRISMoments(ISISIndirectInelasticMoments): def __init__(self): @@ -743,8 +718,8 @@ class IRISMoments(ISISIndirectInelasticMoments): def get_reference_files(self): return ['II.IRISMoments.nxs'] - #============================================================================== + class ISISIndirectInelasticElwinAndMSDFit(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic Elwin/MSD Fit tests @@ -810,7 +785,6 @@ class ISISIndirectInelasticElwinAndMSDFit(ISISIndirectInelasticBase): #------------------------- OSIRIS tests --------------------------------------- - class OSIRISElwinAndMSDFit(ISISIndirectInelasticElwinAndMSDFit): def __init__(self): @@ -828,7 +802,6 @@ class OSIRISElwinAndMSDFit(ISISIndirectInelasticElwinAndMSDFit): #------------------------- IRIS tests ----------------------------------------- - class IRISElwinAndMSDFit(ISISIndirectInelasticElwinAndMSDFit): def __init__(self): @@ -844,8 +817,8 @@ class IRISElwinAndMSDFit(ISISIndirectInelasticElwinAndMSDFit): 'II.IRISElwinEQ2.nxs', 'II.IRISMSDFit.nxs'] - #============================================================================== + class ISISIndirectInelasticFuryAndFuryFit(ISISIndirectInelasticBase): ''' A base class for the ISIS indirect inelastic Fury/FuryFit tests @@ -917,7 +890,6 @@ class ISISIndirectInelasticFuryAndFuryFit(ISISIndirectInelasticBase): #------------------------- OSIRIS tests --------------------------------------- - class OSIRISFuryAndFuryFit(ISISIndirectInelasticFuryAndFuryFit): def __init__(self): @@ -943,7 +915,6 @@ class OSIRISFuryAndFuryFit(ISISIndirectInelasticFuryAndFuryFit): #------------------------- IRIS tests ----------------------------------------- - class IRISFuryAndFuryFit(ISISIndirectInelasticFuryAndFuryFit): def __init__(self): @@ -969,7 +940,6 @@ class IRISFuryAndFuryFit(ISISIndirectInelasticFuryAndFuryFit): #============================================================================== - class ISISIndirectInelasticFuryAndFuryFitMulti(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic Fury/FuryFit tests @@ -1039,7 +1009,6 @@ class ISISIndirectInelasticFuryAndFuryFitMulti(ISISIndirectInelasticBase): #------------------------- OSIRIS tests --------------------------------------- - class OSIRISFuryAndFuryFitMulti(ISISIndirectInelasticFuryAndFuryFitMulti): def skipTests(self): @@ -1068,7 +1037,6 @@ class OSIRISFuryAndFuryFitMulti(ISISIndirectInelasticFuryAndFuryFitMulti): #------------------------- IRIS tests ----------------------------------------- - class IRISFuryAndFuryFitMulti(ISISIndirectInelasticFuryAndFuryFitMulti): def __init__(self): @@ -1094,7 +1062,6 @@ class IRISFuryAndFuryFitMulti(ISISIndirectInelasticFuryAndFuryFitMulti): #============================================================================== - class ISISIndirectInelasticConvFit(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic ConvFit tests @@ -1149,7 +1116,6 @@ class ISISIndirectInelasticConvFit(ISISIndirectInelasticBase): #------------------------- OSIRIS tests --------------------------------------- - class OSIRISConvFit(ISISIndirectInelasticConvFit): def __init__(self): @@ -1172,8 +1138,8 @@ class OSIRISConvFit(ISISIndirectInelasticConvFit): def get_reference_files(self): return ['II.OSIRISConvFitSeq.nxs'] - #------------------------- IRIS tests ----------------------------------------- + class IRISConvFit(ISISIndirectInelasticConvFit): def __init__(self): @@ -1199,7 +1165,6 @@ class IRISConvFit(ISISIndirectInelasticConvFit): #============================================================================== - class ISISIndirectInelasticApplyCorrections(ISISIndirectInelasticBase): '''A base class for the ISIS indirect inelastic Apply Corrections tests @@ -1282,7 +1247,6 @@ class IRISApplyCorrectionsWithCan(ISISIndirectInelasticApplyCorrections): def get_reference_files(self): return ['II.IRISApplyCorrectionsWithCan.nxs'] - class IRISApplyCorrectionsWithCorrectionsWS(ISISIndirectInelasticApplyCorrections): """ Test applying corrections with a corrections workspace """ @@ -1323,9 +1287,6 @@ class IRISApplyCorrectionsWithBoth(ISISIndirectInelasticApplyCorrections): # Transmission Monitor Test class ISISIndirectInelasticTransmissionMonitor(ISISIndirectInelasticBase): - ''' - ''' - # Mark as an abstract class __metaclass__ = ABCMeta @@ -1347,8 +1308,8 @@ class ISISIndirectInelasticTransmissionMonitor(ISISIndirectInelasticBase): if type(self.can) != str: raise RuntimeError("Can should be a string.") - #------------------------- IRIS tests ----------------------------------------- + class IRISTransmissionMonitor(ISISIndirectInelasticTransmissionMonitor): def __init__(self): diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectEnergyConversionTest.py b/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectEnergyConversionTest.py index b213a4662eae77cb5874619666aeb94c3e552642..b683bf46ee8c927ee7ddf8c6df63380d9c88ca77 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectEnergyConversionTest.py +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/IndirectEnergyConversionTest.py @@ -12,16 +12,16 @@ class IndirectEnergyConversionTest(stresstesting.MantidStressTest): files = 'irs21360.raw' rebin_string = '-0.5,0.005,0.5' - InelasticIndirectReduction(InputFiles=files, - RebiNString=rebin_string, - DetectorRange=detector_range, + ISISIndirectEnergyTransfer(InputFiles=files, + RebinString=rebin_string, + SpectraRange=detector_range, Instrument=instrument, Analyser=analyser, Reflection=reflection, - OutputWorkspace='__IndirectEnergyCOnversionTest_out_group') + OutputWorkspace='__IndirectEnergyConversionTest_out_group') def validate(self): self.disableChecking.append('Instrument') self.disableChecking.append('SpectraMap') - return 'irs21360_graphite002_red', 'IndirectEnergyConversionTest.nxs' + return 'IRS21360_graphite002_red', 'IndirectEnergyConversionTest.nxs' diff --git a/Code/Mantid/Testing/SystemTests/tests/analysis/reference/II.TOSCAMultiFileReduction2.nxs.md5 b/Code/Mantid/Testing/SystemTests/tests/analysis/reference/II.TOSCAMultiFileReduction2.nxs.md5 index e1bc4905088eab0399d2e77978e0d0e8fd44da2b..44c6c182cc2ce4dad9e05be1deefd9a15c64eccb 100644 --- a/Code/Mantid/Testing/SystemTests/tests/analysis/reference/II.TOSCAMultiFileReduction2.nxs.md5 +++ b/Code/Mantid/Testing/SystemTests/tests/analysis/reference/II.TOSCAMultiFileReduction2.nxs.md5 @@ -1 +1 @@ -ed6aea923444b6d1c6e5dfd810cb329f \ No newline at end of file +b7c86ee67f2e306c7fffd85403cba548 diff --git a/Code/Mantid/docs/source/algorithms/ISISIndirectEnergyTransfer-v1.rst b/Code/Mantid/docs/source/algorithms/ISISIndirectEnergyTransfer-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..5f295ba1884841e02812a51158544e47785d683c --- /dev/null +++ b/Code/Mantid/docs/source/algorithms/ISISIndirectEnergyTransfer-v1.rst @@ -0,0 +1,42 @@ +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- + +Performs a reduction from raw time of flight to energy transfer for an inelastic +indirect geometry instrument at ISIS. + +Usage +----- + +.. include:: ../usagedata-note.txt + +**Example - IRIS energy conversion** + +.. testcode:: ExIRISReduction + + ISISIndirectEnergyTransfer(InputFiles='IRS21360.raw', + OutputWorkspace='IndirectReductions', + Instrument='IRIS', + Analyser='graphite', + Reflection='002', + SpectraRange=[3, 53]) + + reduction_workspace_names = mtd['IndirectReductions'].getNames() + + for workspace_name in reduction_workspace_names: + print workspace_name + +Output: + +.. testoutput:: ExIRISReduction + + IRS21360_graphite002_red + +.. categories:: diff --git a/Code/Mantid/docs/source/algorithms/InelasticIndirectReduction-v1.rst b/Code/Mantid/docs/source/algorithms/InelasticIndirectReduction-v1.rst deleted file mode 100644 index f5290faa11a9e24551c43df04819679e35995818..0000000000000000000000000000000000000000 --- a/Code/Mantid/docs/source/algorithms/InelasticIndirectReduction-v1.rst +++ /dev/null @@ -1,42 +0,0 @@ -.. algorithm:: - -.. summary:: - -.. alias:: - -.. properties:: - -Description ------------ - -Performs a reduction for an inelastic indirect geometry instrument. - -Usage ------ - -.. include:: ../usagedata-note.txt - -**Example - IRIS energy conversion:** - -.. testcode:: ExIRISReduction - - InelasticIndirectReduction(InputFiles='IRS21360.raw', - OutputWorkspace='IndirectReductions', - Instrument='IRIS', - Analyser='graphite', - Reflection='002', - DetectorRange=[3, 53], - SaveFormats=['nxs']) - - reduction_workspace_names = mtd['IndirectReductions'].getNames() - - for workspace_name in reduction_workspace_names: - print workspace_name - -Output: - -.. testoutput:: ExIRISReduction - - irs21360_graphite002_red - -.. categories::