Skip to content
Snippets Groups Projects
Commit 454523d2 authored by Marina Ganeva's avatar Marina Ganeva
Browse files

Refactoring of mlzutils. Step 1. CompareSampleLogs algorithm.

parent 08a33500
No related branches found
No related tags found
No related merge requests found
Showing with 309 additions and 127 deletions
from mantid.api import PythonAlgorithm, AlgorithmFactory, WorkspaceGroup, MatrixWorkspace
from mantid.kernel import Direction, StringListValidator, StringArrayProperty, StringArrayLengthValidator,\
FloatBoundedValidator
import mantid.simpleapi as api
class CompareSampleLogs(PythonAlgorithm):
"""
Compares specified sample logs for a given list of workspaces
"""
def __init__(self):
"""
Init
"""
PythonAlgorithm.__init__(self)
def category(self):
"""
Returns category
"""
return "PythonAlgorithms;Utility\\Workspaces"
def name(self):
"""
Returns name
"""
return "CompareSampleLogs"
def summary(self):
return "Compares specified sample logs for a given list of workspaces."
def PyInit(self):
validator = StringArrayLengthValidator()
validator.setLengthMin(1) # one group may be given
self.declareProperty(StringArrayProperty(name="InputWorkspaces", direction=Direction.Input, validator=validator),
doc="Comma separated list of workspaces or groups of workspaces.")
self.declareProperty(StringArrayProperty(name="SampleLogs", direction=Direction.Input, validator=validator),
doc="Comma separated list of sample logs to compare.")
self.declareProperty("Tolerance", 1e-3, validator=FloatBoundedValidator(lower=1e-7, upper=1.0),
doc="Tolerance for comparison of double values.")
actions = ['warning', 'error']
self.declareProperty("DoNotMatchAction", "warning", StringListValidator(actions),
doc="Action to perform if sample logs do not match.")
return
def validateInputs(self):
# given workspaces must exist
# and must be public of ExperimentInfo
issues = dict()
workspaces = self.getProperty("InputWorkspaces").value
for wsname in workspaces:
if not api.AnalysisDataService.doesExist(wsname):
issues["InputWorkspaces"] = "Workspace " + wsname + " does not exist."
else:
wks = api.AnalysisDataService.retrieve(wsname)
if isinstance(wks, WorkspaceGroup):
for idx in range(wks.getNumberOfEntries()):
if not isinstance(wks.getItem(idx), MatrixWorkspace):
issues["InputWorkspaces"] = "Group " + wsname + " contains workspaces of unsupported type."
elif not isinstance(wks, MatrixWorkspace):
issues["InputWorkspaces"] = "Type of workspace " + wsname + " is not supported by this algorithm."
return issues
def _expand_groups(self):
workspaces = self.getProperty("InputWorkspaces").value
input_workspaces = []
for wsname in workspaces:
wks = api.AnalysisDataService.retrieve(wsname)
if isinstance(wks, WorkspaceGroup):
input_workspaces.extend(wks.getNames())
else:
input_workspaces.append(wsname)
return input_workspaces
def compare_properties(self, lhs_run, rhs_run, plist, tolerance=5e-3):
"""
checks whether properties match in the given runs, produces warnings
@param lhs_run Left-hand-side run
@param rhs_run Right-hand-side run
@param plist List of properties to compare
"""
lhs_title = ""
rhs_title = ""
if lhs_run.hasProperty('run_title') and rhs_run.hasProperty('run_title'):
lhs_title = lhs_run.getProperty('run_title').value
rhs_title = rhs_run.getProperty('run_title').value
# for TOFTOF run_titles can be identical
if lhs_title == rhs_title:
if lhs_run.hasProperty('run_number') and rhs_run.hasProperty('run_number'):
lhs_title = str(lhs_run.getProperty('run_number').value)
rhs_title = str(rhs_run.getProperty('run_number').value)
for property_name in plist:
if lhs_run.hasProperty(property_name) and rhs_run.hasProperty(property_name):
lhs_property = lhs_run.getProperty(property_name)
rhs_property = rhs_run.getProperty(property_name)
if lhs_property.type == rhs_property.type:
if lhs_property.type == 'string':
if lhs_property.value != rhs_property.value:
message = "Property " + property_name + " does not match! " + \
lhs_title + ": " + lhs_property.value + ", but " + \
rhs_title + ": " + rhs_property.value
print message
self.log().warning(message)
elif lhs_property.type == 'number':
if abs(lhs_property.value - rhs_property.value) > tolerance:
message = "Property " + property_name + " does not match! " + \
lhs_title + ": " + str(lhs_property.value) + ", but " + \
rhs_title + ": " + str(rhs_property.value)
print message
self.log().warning(message)
else:
message = "Property " + property_name + " does not match! " + \
lhs_title + ": " + str(lhs_property.value) + " has type " + \
str(lhs_property.type) + ", but " + rhs_title + ": " + \
str(rhs_property.value) + " has type " + str(rhs_property.type)
print message
self.log().warning(message)
else:
message = "Property " + property_name + " is not present in " +\
lhs_title + " or " + rhs_title + " - skipping comparison."
print message
self.log().warning(message)
return
def compare_mandatory(self, wslist, plist, tolerance=0.01):
"""
Compares properties which are required to be the same.
Produces error message and throws exception if difference is observed
or if one of the sample logs is not found.
Important: exits after the first difference is observed. No further check is performed.
@param wslist List of workspaces
@param plist List of properties to compare
@param tolerance Tolerance for comparison of the double values.
"""
# retrieve the workspaces, form dictionary {wsname: run}
runs = {}
for wsname in wslist:
wks = api.AnalysisDataService.retrieve(wsname)
runs[wsname] = wks.getRun()
for prop in plist:
properties = []
for wsname in wslist:
run = runs[wsname]
if not run.hasProperty(prop):
message = "Workspace " + wsname + " does not have sample log " + prop
self.log().error(message)
raise RuntimeError(message)
curprop = run.getProperty(prop)
if curprop.type == 'string':
properties.append(curprop.value)
elif curprop.type == 'number':
properties.append(int(curprop.value/tolerance))
else:
message = "Unknown type " + str(curprop.type) + " for the sample log " +\
prop + " in the workspace " + wsname
self.log().error(message)
raise RuntimeError(message)
# this should never happen, but lets check
nprop = len(properties)
if nprop != len(wslist):
message = "Error. Number of properties " + str(nprop) + " for property " + prop +\
" is not equal to number of workspaces " + str(len(wslist))
self.log().error(message)
raise RuntimeError(message)
pvalue = properties[0]
if properties.count(pvalue) != nprop:
message = "Sample log " + prop + " is not identical in the given list of workspaces. \n" +\
"Workspaces: " + ", ".join(wslist) + "\n Values: " + str(properties)
self.log().error(message)
raise RuntimeError(message)
def PyExec(self):
wslist = self._expand_groups()
# no sence to compare sample logs for one workspace
if len(wslist) < 2:
message = "At least 2 workspaces must be given as an input."
self.log().error(message)
raise RuntimeError(message)
lognames = self.getProperty("SampleLogs").value
tolerance = self.getProperty("Tolerance").value
action = self.getProperty("DoNotMatchAction").value
if action == 'error':
self.compare_mandatory(wslist, lognames, tolerance)
if action == 'warning':
ws1 = api.AnalysisDataService.retrieve(wslist[0])
run1 = ws1.getRun()
for wsname in wslist[1:]:
wks = api.AnalysisDataService.retrieve(wsname)
run = wks.getRun()
self.compare_properties(run1, run, lognames, tolerance)
return
# Register algorithm with Mantid
AlgorithmFactory.subscribe(CompareSampleLogs)
......@@ -141,15 +141,12 @@ class DNSDetEffCorrVana(PythonAlgorithm):
wslist = [self.dataws.getName(), self.vanaws.getName(), self.bkgws.getName()]
mlzutils.same_dimensions(wslist)
# check if the _NORM workspaces exist
wslist = [self.vanaws.getName() + '_NORM', self.bkgws.getName() + '_NORM']
mlzutils.ws_exist(wslist, self.log())
wslist_norm = [self.vanaws.getName() + '_NORM', self.bkgws.getName() + '_NORM']
mlzutils.ws_exist(wslist_norm, self.log())
# check sample logs, produce warnings if different
drun = self.dataws.getRun()
vrun = self.vanaws.getRun()
brun = self.bkgws.getRun()
mlzutils.compare_properties(drun, vrun, self.properties_to_compare, self.log())
mlzutils.compare_properties(vrun, brun, self.properties_to_compare, self.log())
api.CompareSampleLogs(wslist, self.properties_to_compare, 5e-3, 'warning')
# apply correction
outws = self._vana_correct()
if not outws:
......
......@@ -148,12 +148,7 @@ class DNSFlippingRatioCorr(PythonAlgorithm):
self._flipper_valid()
# algorithm must warn if some properties_to_compare are different
ws1 = api.AnalysisDataService.retrieve(self.input_workspaces.values()[0])
run1 = ws1.getRun()
for wsname in self.input_workspaces.values()[1:]:
wks = api.AnalysisDataService.retrieve(wsname)
run = wks.getRun()
mlzutils.compare_properties(run1, run, self.properties_to_compare, self.log())
api.CompareSampleLogs(self.input_workspaces.values(), self.properties_to_compare, 5e-3, 'warning')
return True
def _fr_correction(self):
......
......@@ -84,12 +84,8 @@ class DNSMergeRuns(PythonAlgorithm):
self._same_wavelength()
# algorithm must warn if some properties_to_compare are different
ws1 = api.AnalysisDataService.retrieve(self.workspace_names[0])
run1 = ws1.getRun()
for wsname in self.workspace_names[1:]:
wks = api.AnalysisDataService.retrieve(wsname)
run = wks.getRun()
mlzutils.compare_properties(run1, run, self.properties_to_compare, self.log())
api.CompareSampleLogs(self.workspace_names, self.properties_to_compare, 5e-3, 'warning')
return True
def _same_wavelength(self):
......
......@@ -71,7 +71,7 @@ class TOFTOFMergeRuns(PythonAlgorithm):
Checks whether given workspaces can be merged
"""
# mandatory properties must be identical
mlzutils.compare_mandatory(wsnames, self.mandatory_properties, self.log())
api.CompareSampleLogs(wsnames, self.mandatory_properties, 0.01, 'error')
# timing (x-axis binning) must match
# is it possible to use WorkspaceHelpers::matchingBins from python?
......@@ -89,12 +89,7 @@ class TOFTOFMergeRuns(PythonAlgorithm):
raise RuntimeError(message)
# warnig if optional properties are not identical must be given
ws1 = api.AnalysisDataService.retrieve(wsnames[0])
run1 = ws1.getRun()
for wsname in wsnames[1:]:
wks = api.AnalysisDataService.retrieve(wsname)
run = wks.getRun()
mlzutils.compare_properties(run1, run, self.optional_properties, self.log(), tolerance=0.01)
api.CompareSampleLogs(wsnames, self.optional_properties, 0.01, 'warning')
return True
def PyExec(self):
......
......@@ -57,107 +57,6 @@ def ws_exist(wslist, logger):
return True
def compare_properties(lhs_run, rhs_run, plist, logger, tolerance=5e-3):
"""
checks whether properties match in the given runs, produces warnings
@param lhs_run Left-hand-side run
@param rhs_run Right-hand-side run
@param plist List of properties to compare
@param logger Logger self.log()
"""
lhs_title = ""
rhs_title = ""
if lhs_run.hasProperty('run_title') and rhs_run.hasProperty('run_title'):
lhs_title = lhs_run.getProperty('run_title').value
rhs_title = rhs_run.getProperty('run_title').value
# for TOFTOF run_titles can be identical
if lhs_title == rhs_title:
if lhs_run.hasProperty('run_number') and rhs_run.hasProperty('run_number'):
lhs_title = str(lhs_run.getProperty('run_number').value)
rhs_title = str(rhs_run.getProperty('run_number').value)
for property_name in plist:
if lhs_run.hasProperty(property_name) and rhs_run.hasProperty(property_name):
lhs_property = lhs_run.getProperty(property_name)
rhs_property = rhs_run.getProperty(property_name)
if lhs_property.type == rhs_property.type:
if lhs_property.type == 'string':
if lhs_property.value != rhs_property.value:
message = "Property " + property_name + " does not match! " + \
lhs_title + ": " + lhs_property.value + ", but " + \
rhs_title + ": " + rhs_property.value
logger.warning(message)
elif lhs_property.type == 'number':
if abs(lhs_property.value - rhs_property.value) > tolerance:
message = "Property " + property_name + " does not match! " + \
lhs_title + ": " + str(lhs_property.value) + ", but " + \
rhs_title + ": " + str(rhs_property.value)
logger.warning(message)
else:
message = "Property " + property_name + " does not match! " + \
lhs_title + ": " + str(lhs_property.value) + " has type " + \
str(lhs_property.type) + ", but " + rhs_title + ": " + \
str(rhs_property.value) + " has type " + str(rhs_property.type)
logger.warning(message)
else:
message = "Property " + property_name + " is not present in " +\
lhs_title + " or " + rhs_title + " - skipping comparison."
logger.warning(message)
return
def compare_mandatory(wslist, plist, logger, tolerance=0.01):
"""
Compares properties which are required to be the same.
Produces error message and throws exception if difference is observed
or if one of the sample logs is not found.
Important: exits after the first difference is observed. No further check is performed.
@param wslist List of workspaces
@param plist List of properties to compare
@param logger Logger self.log()
@param tolerance Tolerance for comparison of the double values.
"""
# retrieve the workspaces, form dictionary {wsname: run}
runs = {}
for wsname in wslist:
wks = api.AnalysisDataService.retrieve(wsname)
runs[wsname] = wks.getRun()
for prop in plist:
properties = []
for wsname in wslist:
run = runs[wsname]
if not run.hasProperty(prop):
message = "Workspace " + wsname + " does not have sample log " + prop
logger.error(message)
raise RuntimeError(message)
curprop = run.getProperty(prop)
if curprop.type == 'string':
properties.append(curprop.value)
elif curprop.type == 'number':
properties.append(int(curprop.value/tolerance))
else:
message = "Unknown type " + str(curprop.type) + " for the sample log " +\
prop + " in the workspace " + wsname
logger.error(message)
raise RuntimeError(message)
# this should never happen, but lets check
nprop = len(properties)
if nprop != len(wslist):
message = "Error. Number of properties " + str(nprop) + " for property " + prop +\
" is not equal to number of workspaces " + str(len(wslist))
logger.error(message)
raise RuntimeError(message)
pvalue = properties[0]
if properties.count(pvalue) != nprop:
message = "Sample log " + prop + " is not identical in the given list of workspaces. \n" +\
"Workspaces: " + ", ".join(wslist) + "\n Values: " + str(properties)
logger.error(message)
raise RuntimeError(message)
def do_fit_gaussian(workspace, index, logger):
"""
Calculates guess values on peak centre, sigma and peak height.
......
......@@ -8,6 +8,7 @@ set ( TEST_PY_FILES
CalculateSampleTransmissionTest.py
CheckForSampleLogsTest.py
ConjoinSpectraTest.py
CompareSampleLogsTest.py
ComputeCalibrationCoefVanTest.py
CorrectLogTimesTest.py
CreateLeBailFitInputTest.py
......
import unittest
from testhelpers import run_algorithm
from mantid.api import AnalysisDataService
from mantid.simpleapi import CreateSampleWorkspace, AddSampleLogMultiple, DeleteWorkspace, GroupWorkspaces
class CompareSampleLogsTest(unittest.TestCase):
def setUp(self):
ws1 = CreateSampleWorkspace()
self.ws1 = ws1
ws2 = CreateSampleWorkspace()
self.ws2 = ws2
lognames = 'run_title,deterota,wavelength,polarisation,flipper'
logvalues = 'ws1,-10.0,4.2,x,ON'
AddSampleLogMultiple(Workspace=ws1, LogNames=lognames, LogValues=logvalues, ParseType=True)
logvalues = 'ws2,-10.0,4.2,x,OFF'
AddSampleLogMultiple(Workspace=ws2, LogNames=lognames, LogValues=logvalues, ParseType=True)
def test_workspaces(self):
wslist = [self.ws1, self.ws2]
lognames = 'deterota,wavelength,polarisation,flipper'
alg_test = run_algorithm("CompareSampleLogs", InputWorkspaces=wslist, SampleLogs=lognames,
Tolerance=0.01, DoNotMatchAction='warning')
self.assertTrue(alg_test.isExecuted())
def test_groups(self):
GroupWorkspaces([self.ws1, self.ws2], OutputWorkspace='group')
lognames = 'deterota,wavelength,polarisation,flipper'
alg_test = run_algorithm("CompareSampleLogs", InputWorkspaces='group', SampleLogs=lognames,
Tolerance=0.01, DoNotMatchAction='warning')
self.assertTrue(alg_test.isExecuted())
def tearDown(self):
if AnalysisDataService.doesExist('ws1'):
DeleteWorkspace('ws1')
if AnalysisDataService.doesExist('ws2'):
DeleteWorkspace('ws2')
if __name__ == "__main__":
unittest.main()
.. algorithm::
.. summary::
.. alias::
.. properties::
Description
-----------
Utility algorithm. Compares specified sample logs for a given list of workspaces or workspace groups. If sample logs match, no output will be produced. If sample logs do not match or do not exist, one two actions on user's choice will be performed:
- **warning**: algorithm will throw a warning, containing run titles or run numbers of not identical properties. All specified sample logs will be checked for all input workspaces. This action may be utilized to warn the user about difference in optional sample logs.
- **error**: algorithm will terminate with an error message after first not matching property will be found or if one of specified sample logs does not exist. This action is useful for mandatory properties, which must be identical in the given list of workspaces.
Usage
-----
.. testcode:: ExCompareSampleLogs
# create workspaces with some sample logs
ws1 = CreateSampleWorkspace()
ws2 = CreateSampleWorkspace()
lognames = 'run_title,deterota,wavelength,polarisation,flipper'
logvalues = 'ws1,-10.0,4.2,x,ON'
AddSampleLogMultiple(Workspace=ws1, LogNames=lognames, LogValues=logvalues, ParseType=True)
logvalues = 'ws2,-10.0,4.2,x,OFF'
AddSampleLogMultiple(Workspace=ws2, LogNames=lognames, LogValues=logvalues, ParseType=True)
# compare sample logs
CompareSampleLogs('ws1,ws2', 'deterota,wavelength,polarisation,flipper' , 0.01, 'warning')
.. testcleanup:: ExCompareSampleLogs
DeleteWorkspace('ws1')
DeleteWorkspace('ws2')
Output:
.. testoutput:: ExCompareSampleLogs
Property flipper does not match! ws1: ON, but ws2: OFF
.. categories::
.. sourcelink::
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment