New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Indirect QuickRun - DiffScan #19171
Indirect QuickRun - DiffScan #19171
Changes from all commits
3c003a7
2e4afcb
a58f5b0
b0254d5
fed5258
f7085f7
0b9eda7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,284 @@ | ||
from __future__ import (absolute_import, division, print_function) | ||
from mantid.kernel import * | ||
from mantid.api import * | ||
from mantid.simpleapi import * | ||
from mantid import config | ||
|
||
|
||
class IndirectDiffScan(DataProcessorAlgorithm): | ||
_data_files = None | ||
_sum_files = None | ||
_load_logs = None | ||
_calibration_ws = None | ||
_instrument_name = None | ||
_analyser = None | ||
_reflection = None | ||
_efixed = None | ||
_spectra_range = None | ||
_background_range = None | ||
_elastic_range = None | ||
_inelastic_range = None | ||
_rebin_string = None | ||
_detailed_balance = None | ||
_grouping_method = None | ||
_grouping_ws = None | ||
_grouping_map_file = None | ||
_output_ws = None | ||
_output_x_units = None | ||
_plot_type = None | ||
_save_formats = None | ||
_ipf_filename = None | ||
_sample_log_name = None | ||
_sample_log_value = None | ||
_workspace_names = None | ||
_scan_ws = None | ||
|
||
def category(self): | ||
return 'Workflow\\Inelastic;Inelastic\\Indirect;Workflow\\MIDAS' | ||
|
||
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='LoadLogFiles', defaultValue=True, | ||
doc='Load log files when loading runs') | ||
|
||
# Instrument configuration properties | ||
self.declareProperty(name='Instrument', defaultValue='', | ||
validator=StringListValidator(['IRIS', 'OSIRIS']), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to try to use the instrument user has set if this property is not specified? The default instrument can be accesses by
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The stringListValidator means they have to select an option. Also the instrument scientists typically swap between data from IRIS and OSIRIS regularly, so it makes sense to have the option mandatory here rather than their scripts picking up the wrong run because they forgot to change the instrument config There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the value of this option actually compared against the loaded files, then? I think I saw the instrument name being read directly from a workspace somewhere in this algorithm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It gets passed to |
||
doc='Instrument used during run.') | ||
|
||
int_arr_valid = IntArrayBoundedValidator() | ||
int_arr_valid.setLower(0) | ||
|
||
self.declareProperty(IntArrayProperty(name='SpectraRange', values=[0, 1], | ||
validator=int_arr_valid), | ||
doc='Comma separated range of spectra number to use.') | ||
|
||
self.declareProperty(name='SampleEnvironmentLogName', defaultValue='sample', | ||
doc='Name of the sample environment log entry') | ||
|
||
sampEnvLogVal_type = ['last_value', 'average'] | ||
self.declareProperty('SampleEnvironmentLogValue', 'last_value', | ||
StringListValidator(sampEnvLogVal_type), | ||
doc='Value selection of the sample environment log entry') | ||
|
||
# Output properties | ||
self.declareProperty(name='OutputWorkspace', defaultValue='Output', | ||
doc='Workspace group for the resulting workspaces.') | ||
|
||
def PyExec(self): | ||
|
||
self._setup() | ||
|
||
process_prog = Progress(self, start=0.1, end=0.9, nreports=len(self._workspace_names)) | ||
process_prog.report("Running diffraction") | ||
scan_alg = self.createChildAlgorithm("ISISIndirectDiffractionReduction", 0.05, 0.95) | ||
scan_alg.setProperty('InputFiles', self._data_files) | ||
scan_alg.setProperty('ContainerFiles', self._can_files) | ||
scan_alg.setProperty('ContainerScaleFactor', self._can_scale) | ||
scan_alg.setProperty('CalFile', self._calib_file) | ||
scan_alg.setProperty('SumFiles', self._sum_files) | ||
scan_alg.setProperty('LoadLogFiles', self._load_logs) | ||
scan_alg.setProperty('Instrument', self._instrument_name) | ||
scan_alg.setProperty('Mode', self._mode) | ||
scan_alg.setProperty('SpectraRange', self._spectra_range) | ||
scan_alg.setProperty('RebinParam', self._rebin_paras) | ||
scan_alg.setProperty('GroupingPolicy', self._grouping_method) | ||
scan_alg.setProperty('OutputWorkspace', self._output_ws) | ||
scan_alg.execute() | ||
logger.information('OutputWorkspace : %s' % self._output_ws) | ||
|
||
self.setProperty('OutputWorkspace', scan_alg.getPropertyValue('OutputWorkspace')) | ||
|
||
workspace_names = mtd[self._output_ws].getNames() | ||
scan_workspace = self._output_ws + '_scan' | ||
temperatures = list() | ||
run_numbers = [] | ||
for input_ws in workspace_names: | ||
temp = self._get_temperature(input_ws) | ||
if temp is not None: | ||
temperatures.append(temp) | ||
# Get the run number | ||
run_no = self._get_InstrRun(input_ws)[1] | ||
run_numbers.append(run_no) | ||
|
||
clone_alg = self.createChildAlgorithm("CloneWorkspace", enableLogging=False) | ||
append_alg = self.createChildAlgorithm("AppendSpectra", enableLogging=False) | ||
for idx in range(len(workspace_names)): | ||
if idx == 0: | ||
clone_alg.setProperty("InputWorkspace", workspace_names[0]) | ||
clone_alg.setProperty("OutputWorkspace", scan_workspace) | ||
clone_alg.execute() | ||
scan_workspace = clone_alg.getProperty("OutputWorkspace").value | ||
else: | ||
append_alg.setProperty("InputWorkspace1", scan_workspace) | ||
append_alg.setProperty("InputWorkspace2", workspace_names[idx]) | ||
append_alg.setProperty("OutputWorkspace", scan_workspace) | ||
append_alg.execute() | ||
scan_workspace = append_alg.getProperty("OutputWorkspace").value | ||
|
||
# Set the vertical axis units | ||
num_hist = scan_workspace.getNumberHistograms() | ||
v_axis_is_temp = num_hist == len(temperatures) | ||
|
||
if v_axis_is_temp: | ||
logger.notice('Vertical axis is in temperature') | ||
unit = ('Temperature', 'K') | ||
else: | ||
logger.notice('Vertical axis is in run number') | ||
unit = ('Run No', '_last 3 digits') | ||
|
||
# Create a new vertical axis for the workspaces | ||
y_ws_axis = NumericAxis.create(len(run_numbers)) | ||
y_ws_axis.setUnit("Label").setLabel(unit[0], unit[1]) | ||
|
||
# Set the vertical axis values | ||
for idx in range(num_hist): | ||
if v_axis_is_temp: | ||
y_ws_axis.setValue(idx, float(temperatures[idx])) | ||
else: | ||
y_ws_axis.setValue(idx, float(run_numbers[idx][-3:])) | ||
|
||
# Add the new vertical axis to each workspace | ||
scan_workspace.replaceAxis(1, y_ws_axis) | ||
|
||
mtd.addOrReplace(self._output_ws + '_scan', scan_workspace) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the final output of the algorithm? You should rather do it using setProperty('OutputWorkspace', scan_workspace) Also, in its current form, this is broken: outWS = IndirectDiffScan(InputFiles=['26181','26182'],
Instrument='IRIS',
SpectraRange=[105, 112]) In the above, If forcing a suffix upon the output workspace is really desired, one could consider having an |
||
|
||
def validateInputs(self): | ||
""" | ||
Validates algorithm properties. | ||
""" | ||
issues = dict() | ||
|
||
# Validate spectra range | ||
spectra_range = self.getProperty('SpectraRange').value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be worth checking that numbers in SpectraRange are non-negative as well. |
||
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' | ||
|
||
return issues | ||
|
||
def _get_temperature(self, ws_name): | ||
""" | ||
Gets the sample temperature for a given workspace. | ||
|
||
@param ws_name Name of workspace | ||
@returns Temperature in Kelvin or None if not found | ||
""" | ||
instr, run_number = self._get_InstrRun(ws_name) | ||
|
||
facility = config.getFacility() | ||
pad_num = facility.instrument(instr).zeroPadding(int(run_number)) | ||
zero_padding = '0' * (pad_num - len(run_number)) | ||
|
||
run_name = instr + zero_padding + run_number | ||
log_filename = run_name.upper() + '.log' | ||
|
||
run = mtd[ws_name].getRun() | ||
|
||
if self._sample_log_name in run: | ||
# Look for temperature in logs in workspace | ||
tmp = run[self._sample_log_name].value | ||
value_action = {'last_value': lambda x: x[-1], | ||
'average': lambda x: x.mean() | ||
} | ||
temp = value_action[self._sample_log_value](tmp) | ||
logger.debug('Temperature %d K found for run: %s' % (temp, run_name)) | ||
return temp | ||
|
||
else: | ||
# Logs not in workspace, try loading from file | ||
logger.information('Log parameter not found in workspace. Searching for log file.') | ||
log_path = FileFinder.getFullPath(log_filename) | ||
|
||
if log_path != '': | ||
# Get temperature from log file | ||
LoadLog(Workspace=ws_name, Filename=log_path) | ||
run_logs = mtd[ws_name].getRun() | ||
if self._sample_log_name in run_logs: | ||
tmp = run_logs[self._sample_log_name].value | ||
temp = tmp[-1] | ||
logger.debug('Temperature %d K found for run: %s' % (temp, run_name)) | ||
return temp | ||
else: | ||
logger.warning('Log entry %s for run %s not found' % (self._sample_log_name, run_name)) | ||
else: | ||
logger.warning('Log file for run %s not found' % run_name) | ||
|
||
# Can't find log file | ||
logger.warning('No temperature found for run: %s' % run_name) | ||
return None | ||
|
||
def _get_InstrRun(self, ws_name): | ||
""" | ||
Get the instrument name and run number from a workspace. | ||
|
||
@param ws_name - name of the workspace | ||
@return tuple of form (instrument, run number) | ||
""" | ||
|
||
run_number = str(mtd[ws_name].getRunNumber()) | ||
if run_number == '0': | ||
# Attempt to parse run number off of name | ||
match = re.match(r'([a-zA-Z]+)([0-9]+)', ws_name) | ||
if match: | ||
run_number = match.group(2) | ||
else: | ||
raise RuntimeError("Could not find run number associated with workspace.") | ||
|
||
instrument = mtd[ws_name].getInstrument().getName() | ||
if instrument != '': | ||
for facility in config.getFacilities(): | ||
try: | ||
instrument = facility.instrument(instrument).filePrefix(int(run_number)) | ||
instrument = instrument.lower() | ||
break | ||
except RuntimeError: | ||
continue | ||
|
||
return instrument, run_number | ||
|
||
def _setup(self): | ||
""" | ||
Gets algorithm properties. | ||
""" | ||
|
||
# Get properties | ||
self._data_files = self.getProperty('InputFiles').value | ||
self._can_files = '' | ||
self._can_scale = 1.0 | ||
self._calib_file = '' | ||
self._sum_files = False | ||
self._load_logs = self.getProperty('LoadLogFiles').value | ||
|
||
self._instrument_name = self.getPropertyValue('Instrument') | ||
self._mode = 'diffspec' | ||
|
||
self._spectra_range = self.getProperty('SpectraRange').value | ||
self._rebin_paras = '' | ||
|
||
self._grouping_method = 'All' | ||
|
||
self._sample_log_name = self.getPropertyValue('SampleEnvironmentLogName') | ||
self._sample_log_value = self.getPropertyValue('SampleEnvironmentLogValue') | ||
|
||
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 | ||
|
||
# The list of workspaces being processed | ||
self._workspace_names = [] | ||
|
||
|
||
# Register algorithm with Mantid | ||
AlgorithmFactory.subscribe(IndirectDiffScan) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from __future__ import (absolute_import, division, print_function) | ||
|
||
import unittest | ||
from mantid.simpleapi import * | ||
from mantid.api import * | ||
|
||
|
||
class IndirectDiffScanTest(unittest.TestCase): | ||
def test_basic_reduction_completes(self): | ||
""" | ||
Sanity test to ensure the most basic reduction actually completes. | ||
""" | ||
|
||
IndirectDiffScan(InputFiles=['IRS26176.RAW'], | ||
Instrument='IRIS', | ||
SpectraRange=[105, 112]) | ||
|
||
wks = mtd['Output'] | ||
self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') | ||
self.assertEqual(len(wks), 1) | ||
self.assertEqual(wks.getNames()[0], 'iris26176_diffspec_red') | ||
|
||
red_ws = wks[0] | ||
self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') | ||
self.assertEqual(red_ws.getNumberHistograms(), 1) | ||
|
||
scan_ws = mtd['Output_scan'] | ||
self.assertEqual(round(scan_ws.readY(0)[0], 7), 6.6324986) | ||
|
||
def test_multi_files(self): | ||
""" | ||
Test reducing multiple files. | ||
""" | ||
|
||
IndirectDiffScan(InputFiles=['IRS26176.RAW', 'IRS26173.RAW'], | ||
Instrument='IRIS', | ||
SpectraRange=[105, 112]) | ||
|
||
wks = mtd['Output'] | ||
self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') | ||
self.assertEqual(len(wks), 2) | ||
self.assertEqual(wks.getNames()[0], 'iris26176_diffspec_red') | ||
self.assertEqual(wks.getNames()[1], 'iris26173_diffspec_red') | ||
|
||
red_ws = wks[0] | ||
self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') | ||
self.assertEqual(red_ws.getNumberHistograms(), 1) | ||
|
||
scan_ws = mtd['Output_scan'] | ||
self.assertEqual(round(scan_ws.readY(0)[0], 7), 6.6324986) | ||
|
||
|
||
def test_osiris(self): | ||
""" | ||
Test with OSIRIS | ||
""" | ||
IndirectDiffScan(InputFiles=['osi89813.raw'], | ||
Instrument='OSIRIS', | ||
SpectraRange=[100, 150]) | ||
|
||
wks = mtd['Output'] | ||
self.assertTrue(isinstance(wks, WorkspaceGroup), 'Result workspace should be a workspace group.') | ||
self.assertEqual(len(wks), 1) | ||
self.assertEqual(wks.getNames()[0], 'osiris89813_diffspec_red') | ||
|
||
red_ws = wks[0] | ||
self.assertEqual(red_ws.getAxis(0).getUnit().unitID(), 'dSpacing') | ||
self.assertEqual(red_ws.getNumberHistograms(), 1) | ||
|
||
|
||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For Python 3, it might be worth starting this source file with