Commit b1e0aade authored by Samuel Jackson's avatar Samuel Jackson
Browse files

Merge branch 'master' into 20441-sort-i-over-sigma

parents 85cf9eff 7ce53f97
......@@ -109,7 +109,7 @@ public:
/// Get a list of events for a given Q
boost::optional<const std::vector<std::pair<double, Mantid::Kernel::V3D>> &>
const std::vector<std::pair<double, Mantid::Kernel::V3D>> *
getEvents(const Mantid::Kernel::V3D &peak_q);
bool correctForDetectorEdges(std::tuple<double, double, double> &radii,
......@@ -87,7 +87,7 @@ Integrate3DEvents::integrateStrongPeak(const IntegrationParameters &params,
return std::make_pair(boost::make_shared<NoShape>(),
make_tuple(0., 0., 0.));
const auto &events = result.get();
const auto &events = *result;
if (events.empty())
return std::make_pair(boost::make_shared<NoShape>(),
make_tuple(0., 0., 0.));
......@@ -183,7 +183,7 @@ Integrate3DEvents::integrateWeakPeak(
if (!result)
return boost::make_shared<NoShape>();
const auto &events = result.get();
const auto &events = *result;
const auto &directions = shape->directions();
const auto &abcBackgroundInnerRadii = shape->abcRadiiBackgroundInner();
......@@ -238,7 +238,7 @@ double Integrate3DEvents::estimateSignalToNoiseRatio(
if (!result)
return .0;
const auto &events = result.get();
const auto &events = *result;
if (events.empty())
return .0;
......@@ -282,23 +282,22 @@ double Integrate3DEvents::estimateSignalToNoiseRatio(
return inti / std::max(1.0, (ratio * backgrd));
boost::optional<const std::vector<std::pair<double, V3D>> &>
const std::vector<std::pair<double, V3D>> *
Integrate3DEvents::getEvents(const V3D &peak_q) {
const auto hkl_key = getHklKey(peak_q);
if (hkl_key == 0)
return boost::optional<const std::vector<std::pair<double, V3D>> &>();
return nullptr;
const auto pos = m_event_lists.find(hkl_key);
using EventListType = const decltype(pos->second) &;
if (m_event_lists.end() == pos)
return boost::optional<EventListType>();
return nullptr;
if (pos->second.size() < 3) // if there are not enough events
return boost::optional<EventListType>();
return nullptr;
return boost::make_optional<EventListType>(pos->second);
return &(pos->second);
bool Integrate3DEvents::correctForDetectorEdges(
......@@ -21,6 +21,7 @@ and assign it to the rebinned variable
from __future__ import (absolute_import, division,
import six
from six import iteritems
from collections import OrderedDict, namedtuple
import os
......@@ -66,11 +67,8 @@ def extract_progress_kwargs(kwargs):
keywords removed from kwargs. If the progress keywords are not
specified, None will be returned in their place.
start = kwargs.get('startProgress')
end = kwargs.get('endProgress')
for item in ('startProgress', 'endProgress'):
if item in kwargs:
del kwargs[item]
start = kwargs.pop('startProgress', None)
end = kwargs.pop('endProgress', None)
return start, end, kwargs
......@@ -170,10 +168,8 @@ def Load(*args, **kwargs):
'you might have forgotten to add its location in the data search '
# Remove from keywords so it is not set twice
if 'Filename' in kwargs:
del kwargs['Filename']
except KeyError:
lhs = _kernel.funcinspect.lhs_info()
# If the output has not been assigned to anything, i.e. lhs[0] = 0 and kwargs does not have OutputWorkspace
# then raise a more helpful error than what we would get from an algorithm
......@@ -269,9 +265,9 @@ def StartLiveData(*args, **kwargs):
def handleSpecialProperty(name, value=None):
if value is None:
value = kwargs[name]
value = kwargs.pop(name)
algm.setProperty(name, value)
kwargs.pop(name, None)
except ValueError as ve:
raise ValueError('Problem when setting %s. This is the detailed error '
'description: %s' % (name, str(ve)))
......@@ -308,6 +304,7 @@ def StartLiveData(*args, **kwargs):
# ---------------------------- Fit ---------------------------------------------
def fitting_algorithm(inout=False):
Decorator generating code for fitting algorithms (Fit, CalculateChiSquared,
......@@ -322,7 +319,8 @@ def fitting_algorithm(inout=False):
def wrapper(*args, **kwargs):
function, input_workspace = _get_mandatory_args(function_name,
["Function", "InputWorkspace"], *args, **kwargs)
["Function", "InputWorkspace"],
*args, **kwargs)
# Remove from keywords so it is not set twice
if "Function" in kwargs:
del kwargs['Function']
......@@ -436,7 +434,10 @@ def FitDialog(*args, **kwargs):
- Disable :: A CSV list of properties to disable in the dialog
- Message :: An optional message string
arguments = {}
# default values will be overridden
arguments = {'Enable':'',
function, inputworkspace = _get_mandatory_args('FitDialog', ['Function', 'InputWorkspace'], *args, **kwargs)
arguments['Function'] = function
......@@ -444,12 +445,6 @@ def FitDialog(*args, **kwargs):
except RuntimeError:
if 'Enable' not in arguments:
arguments['Enable'] = ''
if 'Disable' not in arguments:
arguments['Disable'] = ''
if 'Message' not in arguments:
arguments['Message'] = ''
(_startProgress, _endProgress, kwargs) = extract_progress_kwargs(kwargs)
......@@ -572,6 +567,7 @@ def CutMD(*args, **kwargs):
return out_names[0]
# enddef
_replace_signature(CutMD, ("\bInputWorkspace", "**kwargs"))
......@@ -583,19 +579,18 @@ def RenameWorkspace(*args, **kwargs):
arguments = {}
lhs = _kernel.funcinspect.lhs_info()
if lhs[0] > 0:
if 'OutputWorkspace' not in kwargs:
arguments['OutputWorkspace'] = lhs[1][0]
pos_arg = {0: "InputWorkspace", 1: "RenameMonitors"}
pos_arg = {0: "InputWorkspace", 1: "OutputWorkspace", 2: "RenameMonitors"}
# convert positional args to keyword arguments
if lhs[0] > 0 and 'OutputWorkspace' not in kwargs:
arguments['OutputWorkspace'] = lhs[1][0]
for name, value in zip(("InputWorkspace","RenameMonitors"), args):
arguments[name] = value
pos_arg = {0: "InputWorkspace", 1: "OutputWorkspace", 2: "RenameMonitors"}
for name, value in zip(("InputWorkspace","OutputWorkspace","RenameMonitors"), args):
arguments[name] = value
for ind, arg in enumerate(args):
arguments[pos_arg[ind]] = arg
for key, val in kwargs.items():
arguments[key] = val
if 'OutputWorkspace' not in arguments:
raise RuntimeError("Unable to set output workspace name."
" Please either assign the output of "
......@@ -618,6 +613,8 @@ def RenameWorkspace(*args, **kwargs):
return _gather_returns("RenameWorkspace", lhs, algm)
# enddef
_replace_signature(RenameWorkspace, ("\bInputWorkspace,[OutputWorkspace],[True||False]", "**kwargs"))
# --------------------------------------------------- --------------------------
......@@ -629,12 +626,11 @@ def _get_function_spec(func):
:param func: A Python function object
import inspect
import six
if six.PY3:
argspec = inspect.getfullargspec(func)
if six.PY2:
argspec = inspect.getargspec(func)
argspec = inspect.getfullargspec(func)
except TypeError:
return ''
# Algorithm functions have varargs set not args
......@@ -782,6 +778,7 @@ def _is_workspace_property(prop):
# Doesn't look like a workspace property
return False
def _is_function_property(prop):
Returns True if the property is a fit function
......@@ -790,9 +787,8 @@ def _is_function_property(prop):
:type Property
:return: True if the property is considered a fit function
if isinstance(prop, _api.FunctionProperty):
return True
return False
return isinstance(prop, _api.FunctionProperty)
def _get_args_from_lhs(lhs, algm_obj):
......@@ -949,9 +945,7 @@ def _set_logging_option(algm_obj, kwargs):
:param algm_obj: An initialised algorithm object
:param **kwargs: A dictionary of the keyword arguments passed to the simple function call
if __LOGGING_KEYWORD__ in kwargs:
del kwargs[__LOGGING_KEYWORD__]
algm_obj.setLogging(kwargs.pop(__LOGGING_KEYWORD__, True))
def _set_store_ads(algm_obj, kwargs):
......@@ -961,11 +955,7 @@ def _set_store_ads(algm_obj, kwargs):
:param algm_obj: An initialised algorithm object
:param **kwargs: A dictionary of the keyword arguments passed to the simple function call
if __STORE_KEYWORD__ in kwargs:
del kwargs[__STORE_KEYWORD__]
algm_obj.setAlwaysStoreInADS(kwargs.pop(__STORE_KEYWORD__, __STORE_ADS_DEFAULT__))
def set_properties(alg_object, *args, **kwargs):
......@@ -988,12 +978,11 @@ def set_properties(alg_object, *args, **kwargs):
mandatory_props = alg_object.mandatoryProperties()
mandatory_props = []
postponed = []
for (key, value) in iteritems(kwargs):
if key in mandatory_props:
except ValueError:
if "IndexSet" in key:
# The `IndexSet` sub-property of the "workspace property with index"
# must be set after the workspace since it is validated based on in.
......@@ -1002,9 +991,10 @@ def set_properties(alg_object, *args, **kwargs):
do_set_property(key, value)
for (key, value) in postponed:
do_set_property(key, value)
if len(args) > 0:
for (key, value) in zip(mandatory_props[:len(args)], args):
do_set_property(key, value)
# zip stops at the length of the shorter list
for (key, value) in zip(mandatory_props, args):
do_set_property(key, value)
def _create_algorithm_function(name, version, algm_object):
......@@ -1044,11 +1034,7 @@ def _create_algorithm_function(name, version, algm_object):
if "CoordinatesToUse" in kwargs and name in __MDCOORD_FUNCTIONS__:
del kwargs["CoordinatesToUse"]
frame = kwargs["__LHS_FRAME_OBJECT__"]
del kwargs["__LHS_FRAME_OBJECT__"]
except KeyError:
frame = None
frame = kwargs.pop("__LHS_FRAME_OBJECT__", None)
lhs = _kernel.funcinspect.lhs_info(frame=frame)
lhs_args = _get_args_from_lhs(lhs, algm)
......@@ -1070,11 +1056,9 @@ def _create_algorithm_function(name, version, algm_object):
globals()[name] = algm_wrapper
# Register aliases
for alias in algm_object.alias().strip().split(' '):
alias = alias.strip()
if len(alias) > 0:
globals()[alias] = algm_wrapper
# Register aliases - split on whitespace
for alias in algm_object.alias().strip().split():
globals()[alias] = algm_wrapper
# endfor
return algm_wrapper
# -------------------------------------------------------------------------------------------------------------
......@@ -1155,12 +1139,9 @@ def set_properties_dialog(algm_object, *args, **kwargs):
raise RuntimeError("Can only display properties dialog in gui mode")
# generic setup
enabled_list = [s.lstrip(' ') for s in kwargs.get("Enable", "").split(',')]
del kwargs["Enable"] # no longer needed
disabled_list = [s.lstrip(' ') for s in kwargs.get("Disable", "").split(',')]
del kwargs["Disable"] # no longer needed
message = kwargs.get("Message", "")
del kwargs["Message"]
enabled_list = [s.lstrip(' ') for s in kwargs.pop("Enable", "").split(',')] # no longer needed
disabled_list = [s.lstrip(' ') for s in kwargs.pop("Disable", "").split(',')] # no longer needed
message = kwargs.pop("Message", "")
presets = '|'
# -------------------------------------------------------------------------------
......@@ -1180,7 +1161,7 @@ def set_properties_dialog(algm_object, *args, **kwargs):
elif isinstance(value_to_use, tuple):
return str(value_to_use).lstrip('(').rstrip(')')
elif isinstance(value_to_use, bool):
if value_to_use:
if value_to_use: # not sure why these are set to '0' and '1'
return '1'
return '0'
......@@ -1237,12 +1218,10 @@ def _create_algorithm_dialog(algorithm, version, _algm_object):
algm_wrapper = _customise_func(algorithm_wrapper, "%sDialog" % algorithm,
signature, "\n\n%s dialog" % algorithm)
globals()["%sDialog" % algorithm] = algm_wrapper
globals()["{}Dialog".format(algorithm)] = algm_wrapper
# Register aliases
for alias in _algm_object.alias().strip().split(' '):
alias = alias.strip()
if len(alias) > 0:
globals()["%sDialog" % alias] = algm_wrapper
for alias in _algm_object.alias().strip().split(): # split on whitespace
globals()["{}Dialog".format(alias)] = algm_wrapper
# --------------------------------------------------------------------------------------------------
......@@ -1306,7 +1285,6 @@ def _mockup(plugins):
# Start with the loaded C++ algorithms
from mantid.api import AlgorithmFactory
import os
cppalgs = AlgorithmFactory.getRegisteredAlgorithms(True)
from __future__ import (absolute_import, division, print_function)
from mantid.api import FileProperty, WorkspaceProperty, PythonAlgorithm, AlgorithmFactory, FileAction
from mantid.kernel import Direction
from mantid.simpleapi import CreateWorkspace, AddSampleLogMultiple
import numpy
import h5py
class LoadLamp(PythonAlgorithm):
def name(self):
return 'LoadLamp'
def category(self):
return 'DataHandling\\Nexus'
def summary(self):
return 'Loads HDF files exported from LAMP program at the ILL'
def PyInit(self):
self.declareProperty(FileProperty(name="InputFile", defaultValue="", action=FileAction.Load, extensions=["hdf"]))
self.declareProperty(WorkspaceProperty(name="OutputWorkspace", defaultValue="", direction=Direction.Output))
def PyExec(self):
input_file = self.getProperty("InputFile").value
output_ws = self.getPropertyValue("OutputWorkspace")
with h5py.File(input_file, 'r') as hf:
data = numpy.array(hf.get('entry1/data1/DATA'), dtype='float')
if data.ndim > 2:
raise RuntimeError('Data with more than 2 dimensions are not supported.')
errors = numpy.array(hf.get('entry1/data1/errors'), dtype='float')
x = numpy.array(hf.get('entry1/data1/X'), dtype='float')
logs = str(hf.get('entry1/data1/PARAMETERS')[0])
y = numpy.array([0])
nspec = 1
if data.ndim == 2:
y = numpy.array(hf.get('entry1/data1/Y'), dtype='float')
nspec = data.shape[0]
if x.ndim == 1:
x = numpy.tile(x, nspec)
CreateWorkspace(DataX=x, DataY=data, DataE=errors, NSpec=nspec, VerticalAxisUnit='Label',
VerticalAxisValues=y, OutputWorkspace=output_ws)
log_names = []
log_values = []
for log in logs.split('\n'):
split = log.strip().split('=')
if len(split) == 2:
name = split[0]
value = split[1]
if name and value:
if log_names:
AddSampleLogMultiple(Workspace=output_ws, LogNames=log_names, LogValues=log_values)
except RuntimeError as e:
self.log().warning('Unable to set the sample logs, reason: '+str(e))
self.setProperty('OutputWorkspace', output_ws)
......@@ -48,6 +48,7 @@ set ( TEST_PY_FILES
from __future__ import (absolute_import, division, print_function)
from mantid.simpleapi import config, mtd, LoadLamp
import unittest
class LoadLampTest(unittest.TestCase):
def setUp(self):
def tearDown(self):
def test_1D(self):
ws = LoadLamp('967067_LAMP.hdf')
def test_2D_ragged(self):
ws = LoadLamp('967076_LAMP.hdf')
def test_2D_tile(self):
ws = LoadLamp('199902_LAMP.hdf')
def test_3D_fail(self):
self.assertRaises(RuntimeError, LoadLamp, Filename='530333_LAMP.hdf', OutputWorkspace='ws')
if __name__ == '__main__':
.. algorithm::
.. summary::
.. alias::
.. properties::
This algorithm loads processed HDF5 files produced by LAMP software at the ILL. Only 1D and 2D data are supported. The output is a point data workspace.
The input file must have a tree structure similar to:
.. code-block:: python
**Example - LoadLamp**
.. testcode:: ExLoadLamp
ws = LoadLamp('ILL/LAMP/967067_LAMP.hdf')
print("ws has {0} spectrum and {1} points".format(ws.getNumberHistograms(), ws.blocksize()))
.. testoutput:: ExLoadLamp
ws has 1 spectrum and 3200 points
.. testcleanup:: ExLoadLamp
......@@ -40,6 +40,7 @@ Algorithms
- :ref:`Fit <algm-Fit>` has had a bug fixed that prevented a fix from being removed.
- :ref:`LoadMask <algm-LoadMask>` has had a bug fixed that could, under certain conditions, cause detectors from previously loaded masking to be added to the currently loaded masking.
- In :ref:`MaxEnt <algm-MaxEnt>` the ``EvolChi`` and ``EvolAngle`` workspaces only contain data up until the result has converged.
- :ref:`LoadLamp <algm-LoadLamp>` is a new algorithm to load processed HDF5 files produced by LAMP program at ILL.
......@@ -52,7 +53,7 @@ Core Functionality
- Fixed an issue where certain isotopes could not be accessed using the `Atom` classes, e.g Si28.
- Added new functionality to ``datasearch.searcharchive`` :ref:`property <Properties File>` to only search the default facility
- The status of a fit in the fit window is now at the top of the of the dialog instead of the bottom.
- The status of a fit in the fit window is now at the top of the of the dialog instead of the bottom.
- Condition to check if a property is enabled when serializing.
- Workspace locking no longer prevents simple read operations required to display the workspace conext menu in Mantidplot.
......@@ -9,21 +9,28 @@ Reflectometry Changes
putting new features at the top of the section, followed by
improvements, followed by bug fixes.
ISIS Reflectometry Interface
New features
- Menu items and toolbar buttons are now enabled/disabled when appropriate, e.g. to prevent table modification during processing. Directly editing table rows is also disabled during processing.
- Removed the 'DirectBeam' box from the settings tab of the ISIS Reflectometry interface because this is not used.
- Properties on the Runs tab now take precedence over properties on the Settings tab.
- Output workspace names have been improved. Names now use '+' to indicate preprocessed (i.e. summed) workspaces, rather than '_', which is used to indicate postprocessed (i.e. stitched) workspaces.
Bug fixes
- Fixed some bugs where transmission runs entered on the Settings tab were not being found, whether entered as a run number to load or as the name of an existing workspace in the ADS.
......@@ -34,11 +41,13 @@ New features
- The new algorithm :ref:`algm-LoadILLPolarizationFactors` can load the polarization efficiency files used on D17 at ILL.
- The new algorithm :ref:`algm-MRInspectData` takes in raw event data and determines reduction parameters.
- Removed the ``RegionOfDirectBeam`` property from :ref:`algm-ReflectometryReductionOne` and :ref:`algm-ReflectometryReductionOneAuto` because this is not used.
Bug fixes