-
Notifications
You must be signed in to change notification settings - Fork 122
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #509 from mantidproject/11304_createmd
CreateMD
- Loading branch information
Showing
5 changed files
with
473 additions
and
1 deletion.
There are no files selected for viewing
232 changes: 232 additions & 0 deletions
232
Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/CreateMD.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
#pylint: disable=invalid-name,no-init | ||
from mantid.kernel import * | ||
from mantid.api import * | ||
from mantid.simpleapi import * | ||
import numpy as np | ||
import __builtin__ | ||
|
||
|
||
class CreateMD(DataProcessorAlgorithm): | ||
|
||
def _possible_emodes(self): | ||
return ['Elastic', 'Direct', 'Indirect'] | ||
|
||
def _add_sample_log(self, workspace, log_name, log_number): | ||
add_log = self.createChildAlgorithm('AddSampleLog') | ||
add_log.setProperty('Workspace', workspace) | ||
add_log.setProperty('LogName', str(log_name)) | ||
add_log.setProperty('LogText', str(log_number)) | ||
add_log.setProperty('LogType', 'Number') | ||
add_log.execute() | ||
|
||
def _set_goniometer(self, workspace): | ||
|
||
axis0 = ','.join(map(str, ['gl', 0, 0, 1, 1])) | ||
axis1 = ','.join(map(str, ['gs', 1, 0, 0, 1])) | ||
axis2 = ','.join(map(str, ['psi', 0, 1, 0, 1])) | ||
|
||
set_goniometer = self.createChildAlgorithm('SetGoniometer') | ||
set_goniometer.setProperty('Workspace', workspace) | ||
set_goniometer.setProperty('Axis0', axis0) | ||
set_goniometer.setProperty('Axis1', axis1) | ||
set_goniometer.setProperty('Axis2', axis2) | ||
set_goniometer.execute() | ||
|
||
def _set_ub(self, workspace, a, b, c, alpha, beta, gamma, u, v): | ||
set_ub = self.createChildAlgorithm('SetUB') | ||
set_ub.setProperty('Workspace', workspace) | ||
set_ub.setProperty('a', a) | ||
set_ub.setProperty('b', b) | ||
set_ub.setProperty('c', c) | ||
set_ub.setProperty('alpha', alpha) | ||
set_ub.setProperty('beta', beta) | ||
set_ub.setProperty('gamma', gamma) | ||
set_ub.setProperty('u', u) | ||
set_ub.setProperty('v', v) | ||
set_ub.execute() | ||
|
||
def _convert_to_md(self, workspace, analysis_mode): | ||
|
||
# Find the Min Max extents | ||
min_max_alg = self.createChildAlgorithm('ConvertToMDMinMaxGlobal') | ||
min_max_alg.setProperty('InputWorkspace', workspace) | ||
min_max_alg.setProperty('QDimensions', 'Q3D') | ||
min_max_alg.setProperty('dEAnalysisMode', analysis_mode) | ||
min_max_alg.execute() | ||
min_values = min_max_alg.getProperty('MinValues').value | ||
max_values = min_max_alg.getProperty('MaxValues').value | ||
|
||
# Convert to MD | ||
convert_alg = self.createChildAlgorithm('ConvertToMD') | ||
convert_alg.setProperty('InputWorkspace', workspace) | ||
convert_alg.setPropertyValue('OutputWorkspace', 'dummy') | ||
convert_alg.setProperty('QDimensions', 'Q3D') | ||
convert_alg.setProperty('QConversionScales', 'HKL') | ||
convert_alg.setProperty('dEAnalysisMode', analysis_mode) | ||
convert_alg.setProperty('MinValues', min_values) | ||
convert_alg.setProperty('MaxValues', max_values) | ||
convert_alg.execute() | ||
return convert_alg.getProperty('OutputWorkspace').value | ||
|
||
def _merge_runs(self, to_merge): | ||
|
||
merge_alg = self.createChildAlgorithm('MergeMD') | ||
merge_alg.setProperty('InputWorkspaces', to_merge) | ||
merge_alg.setPropertyValue('OutputWorkspace', 'dummy') | ||
merge_alg.execute() | ||
return merge_alg.getProperty('OutputWorkspace').value | ||
|
||
def _single_run(self, input_workspace, emode, psi, gl, gs, alatt=None, angdeg=None, u=None, v=None,): | ||
import numpy as np | ||
ub_params = map(any, [alatt, angdeg, u, v]) | ||
goniometer_params = [psi, gl, gs] | ||
if any(ub_params) and not all(ub_params): | ||
raise ValueError("Either specify all of alatt, angledeg, u, v or none of them") | ||
elif all(ub_params): | ||
if input_workspace.sample().hasOrientedLattice(): | ||
logger.warning("Sample already has a UB. This will not be overwritten by %s. Use ClearUB and re-run."%self.name()) | ||
else: | ||
self._set_ub(workspace=input_workspace, a=alatt[0], b=alatt[1], c=alatt[2], alpha=angdeg[0], beta=angdeg[1], gamma=angdeg[2], u=u, v=v) | ||
|
||
if any(goniometer_params): | ||
self._add_sample_log(workspace=input_workspace, log_name='gl', log_number=gl) | ||
self._add_sample_log(workspace=input_workspace, log_name='gs', log_number=gs) | ||
self._add_sample_log(workspace=input_workspace, log_name='psi', log_number=psi) | ||
self._set_goniometer(workspace=input_workspace) | ||
|
||
output_run = self._convert_to_md(workspace=input_workspace, analysis_mode=emode) | ||
return output_run | ||
|
||
|
||
def category(self): | ||
return 'MDAlgorithms' | ||
|
||
def summary(self): | ||
return 'Creates a mutlidimensional workspace by transforming and combining individual runs.' | ||
|
||
def PyInit(self): | ||
self.declareProperty(StringArrayProperty('InputWorkspaces', values=[], direction=Direction.Input, validator=StringArrayMandatoryValidator()), | ||
doc='Matrix workspace to slice') | ||
|
||
self.declareProperty('Emode', defaultValue='Direct', validator=StringListValidator(self._possible_emodes()), direction=Direction.Input, doc='Analysis mode ' + str(self._possible_emodes()) ) | ||
|
||
self.declareProperty(FloatArrayProperty('Alatt', values=[], validator=FloatArrayMandatoryValidator(), direction=Direction.Input ), doc='Lattice parameters' ) | ||
|
||
self.declareProperty(FloatArrayProperty('Angdeg', values=[], validator=FloatArrayMandatoryValidator(), direction=Direction.Input ), doc='Lattice angles' ) | ||
|
||
self.declareProperty(FloatArrayProperty('u', values=[], validator=FloatArrayMandatoryValidator(), direction=Direction.Input ), doc='Lattice vector parallel to neutron beam' ) | ||
|
||
self.declareProperty(FloatArrayProperty('v', values=[], validator=FloatArrayMandatoryValidator(), direction=Direction.Input ), doc='Lattice vector perpendicular to neutron beam in the horizontal plane' ) | ||
|
||
self.declareProperty(FloatArrayProperty('Psi', values=[], direction=Direction.Input), doc='Psi rotation in degrees. Optional or one entry per run.' ) | ||
|
||
self.declareProperty(FloatArrayProperty('Gl', values=[], direction=Direction.Input), doc='gl rotation in degrees. Optional or one entry per run.' ) | ||
|
||
self.declareProperty(FloatArrayProperty('Gs', values=[], direction=Direction.Input), doc='gs rotation in degrees. Optional or one entry per run.' ) | ||
|
||
self.declareProperty(IMDWorkspaceProperty('OutputWorkspace', '', direction=Direction.Output ), doc='Output MDWorkspace') | ||
|
||
def _validate_inputs(self): | ||
|
||
emode = self.getProperty('Emode').value | ||
alatt = self.getProperty('Alatt').value | ||
angdeg = self.getProperty('Angdeg').value | ||
u = self.getProperty('u').value | ||
v = self.getProperty('v').value | ||
psi = self.getProperty('Psi').value | ||
gl = self.getProperty('Gl').value | ||
gs = self.getProperty('Gs').value | ||
|
||
input_workspaces = self.getProperty("InputWorkspaces").value | ||
|
||
ws_entries = len(input_workspaces) | ||
|
||
if ws_entries < 1: | ||
raise ValueError("Need one or more input workspace") | ||
|
||
if len(u) != 3: | ||
raise ValueError("u must have 3 components") | ||
|
||
if len(v) != 3: | ||
raise ValueError("v must have 3 components") | ||
|
||
if len(alatt) != 3: | ||
raise ValueError("lattice parameters must have 3 components") | ||
|
||
if len(angdeg) != 3: | ||
raise ValueError("Angle must have 3 components") | ||
|
||
if not emode in self._possible_emodes(): | ||
raise ValueError("Unknown emode %s Allowed values are %s" % (emode, self._possible_emodes())) | ||
|
||
if len(psi) > 0 and len(psi) != ws_entries: | ||
raise ValueError("If Psi is given a entry should be provided for every input workspace") | ||
|
||
if len(gl) > 0 and len(gl) != ws_entries: | ||
raise ValueError("If Gl is given a entry should be provided for every input workspace") | ||
|
||
if len(gs) > 0 and len(gs) != ws_entries: | ||
raise ValueError("If Gs is given a entry should be provided for every input workspace") | ||
|
||
|
||
def PyExec(self): | ||
|
||
logger.warning('You are running algorithm %s that is the beta stage of development' % (self.name())) | ||
|
||
emode = self.getProperty('Emode').value | ||
alatt = self.getProperty('Alatt').value | ||
angdeg = self.getProperty('Angdeg').value | ||
u = self.getProperty('u').value | ||
v = self.getProperty('v').value | ||
psi = self.getProperty('Psi').value | ||
gl = self.getProperty('Gl').value | ||
gs = self.getProperty('Gs').value | ||
|
||
input_workspaces = self.getProperty("InputWorkspaces").value | ||
|
||
ws_entries = len(input_workspaces) | ||
|
||
self._validate_inputs() | ||
|
||
if len(psi) == 0: | ||
psi = [0.0] * ws_entries | ||
|
||
if len(gl) == 0: | ||
gl = [0.0] * ws_entries | ||
|
||
if len(gs) == 0: | ||
gs = [0.0] * ws_entries | ||
|
||
output_workspace = None | ||
run_md = None | ||
|
||
to_merge_names = list() | ||
|
||
run_data = zip(input_workspaces, psi, gl, gs) | ||
for run_entry in run_data: | ||
ws_name, psi_entry, gl_entry, gs_entry = run_entry | ||
ws = AnalysisDataService.retrieve(ws_name) | ||
run_md = self._single_run(input_workspace=ws, emode=emode, alatt=alatt, angdeg=angdeg, u=u, v=v, psi=psi_entry, gl=gl_entry, gs=gs_entry) | ||
to_merge_name = ws_name + "_md" | ||
AnalysisDataService.addOrReplace(to_merge_name, run_md) | ||
to_merge_names.append(to_merge_name) | ||
|
||
if len(to_merge_names) > 1: | ||
output_workspace = self._merge_runs(to_merge_names) | ||
else: | ||
output_workspace = AnalysisDataService.retrieve(to_merge_names[0]) | ||
|
||
# Clear out temporary workspaces. | ||
for ws in to_merge_names: | ||
DeleteWorkspace(ws) | ||
|
||
self.setProperty("OutputWorkspace", output_workspace) | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
AlgorithmFactory.subscribe(CreateMD) |
134 changes: 134 additions & 0 deletions
134
Code/Mantid/Framework/PythonInterface/test/python/plugins/algorithms/CreateMDTest.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import unittest | ||
import testhelpers | ||
import numpy as np | ||
from mantid.simpleapi import * | ||
from mantid.api import AlgorithmManager, IMDHistoWorkspace, IMDEventWorkspace | ||
|
||
|
||
class CreateMDTest(unittest.TestCase): | ||
|
||
def test_init(self): | ||
alg = AlgorithmManager.create("CreateMD") | ||
alg.initialize() | ||
|
||
def test_must_have_more_than_one_input_workspace(self): | ||
alg = AlgorithmManager.create("CreateMD") | ||
alg.setRethrows(True) | ||
alg.initialize() | ||
self.assertRaises(ValueError, alg.setProperty, "InputWorkspaces", []) | ||
|
||
def test_set_up_madatory(self): | ||
|
||
alg = AlgorithmManager.create("CreateMD") | ||
alg.setRethrows(True) | ||
alg.initialize() | ||
alg.setPropertyValue("OutputWorkspace", "mdworkspace") | ||
alg.setProperty("InputWorkspaces", ['a', 'b']) | ||
alg.setProperty("Emode", "Direct") | ||
alg.setProperty("Alatt", [1,1,1]) | ||
alg.setProperty("Angdeg", [90,90,90]) | ||
alg.setProperty("u", [0,0,1]) | ||
alg.setProperty("v", [1,0,0]) | ||
|
||
def test_psi_right_size(self): | ||
|
||
input_workspace = CreateSampleWorkspace(NumBanks=1, BinWidth=2000) | ||
AddSampleLog(input_workspace, LogName='Ei', LogText='12.0', LogType='Number') | ||
|
||
alg = AlgorithmManager.create("CreateMD") | ||
alg.setRethrows(True) | ||
alg.initialize() | ||
alg.setPropertyValue("OutputWorkspace", "mdworkspace") | ||
alg.setProperty("InputWorkspaces", ['input_workspace']) | ||
alg.setProperty("Emode", "Direct") | ||
alg.setProperty("Alatt", [1,1,1]) | ||
alg.setProperty("Angdeg", [90,90,90]) | ||
alg.setProperty("u", [0,0,1]) | ||
alg.setProperty("v", [1,0,0]) | ||
alg.setProperty("Psi", [0, 0, 0]) # Too large | ||
alg.setProperty("Gl", [0]) # Right size | ||
alg.setProperty("Gs", [0]) # Right size | ||
self.assertRaises(RuntimeError, alg.execute) | ||
DeleteWorkspace(input_workspace) | ||
|
||
def test_gl_right_size(self): | ||
|
||
input_workspace = CreateSampleWorkspace(NumBanks=1, BinWidth=2000) | ||
AddSampleLog(input_workspace, LogName='Ei', LogText='12.0', LogType='Number') | ||
|
||
alg = AlgorithmManager.create("CreateMD") | ||
alg.setRethrows(True) | ||
alg.initialize() | ||
alg.setPropertyValue("OutputWorkspace", "mdworkspace") | ||
alg.setProperty("InputWorkspaces", ['input_workspace']) | ||
alg.setProperty("Emode", "Direct") | ||
alg.setProperty("Alatt", [1,1,1]) | ||
alg.setProperty("Angdeg", [90,90,90]) | ||
alg.setProperty("u", [0,0,1]) | ||
alg.setProperty("v", [1,0,0]) | ||
alg.setProperty("Psi", [0]) # Right size | ||
alg.setProperty("Gl", [0, 0]) # Too many | ||
alg.setProperty("Gs", [0]) # Right size | ||
self.assertRaises(RuntimeError, alg.execute) | ||
DeleteWorkspace(input_workspace) | ||
|
||
def test_gs_right_size(self): | ||
|
||
input_workspace = CreateSampleWorkspace(NumBanks=1, BinWidth=2000) | ||
AddSampleLog(input_workspace, LogName='Ei', LogText='12.0', LogType='Number') | ||
|
||
alg = AlgorithmManager.create("CreateMD") | ||
alg.setRethrows(True) | ||
alg.initialize() | ||
alg.setPropertyValue("OutputWorkspace", "mdworkspace") | ||
alg.setProperty("InputWorkspaces", ['input_workspace']) | ||
alg.setProperty("Emode", "Direct") | ||
alg.setProperty("Alatt", [1,1,1]) | ||
alg.setProperty("Angdeg", [90,90,90]) | ||
alg.setProperty("u", [0,0,1]) | ||
alg.setProperty("v", [1,0,0]) | ||
alg.setProperty("Psi", [0]) # Right size | ||
alg.setProperty("Gl", [0]) # Right size | ||
alg.setProperty("Gs", [0,0]) # Too large | ||
self.assertRaises(RuntimeError, alg.execute) | ||
DeleteWorkspace(input_workspace) | ||
|
||
|
||
def test_execute_single_workspace(self): | ||
|
||
input_workspace = CreateSampleWorkspace(NumBanks=1, BinWidth=2000) | ||
AddSampleLog(input_workspace, LogName='Ei', LogText='12.0', LogType='Number') | ||
|
||
alg = AlgorithmManager.create("CreateMD") | ||
alg.setRethrows(True) | ||
alg.initialize() | ||
alg.setPropertyValue("OutputWorkspace", "mdworkspace") | ||
alg.setProperty("InputWorkspaces", ['input_workspace']) | ||
alg.setProperty("Alatt", [1,1,1]) | ||
alg.setProperty("Angdeg", [90,90,90]) | ||
alg.setProperty("u", [0,0,1]) | ||
alg.setProperty("v", [1,0,0]) | ||
alg.execute() | ||
out_ws = AnalysisDataService.retrieve("mdworkspace") | ||
|
||
self.assertTrue(isinstance(out_ws, IMDEventWorkspace), "Expected an MDEventWorkspace back") | ||
DeleteWorkspace(input_workspace) | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.