Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/feature/9711_refactor_poldi_merge'
Browse files Browse the repository at this point in the history
  • Loading branch information
mantid-roman committed Oct 24, 2014
2 parents dde0f6d + e2a3548 commit 79ccf37
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 24 deletions.
100 changes: 77 additions & 23 deletions Code/Mantid/Framework/PythonInterface/plugins/algorithms/PoldiMerge.py
Expand Up @@ -5,8 +5,12 @@
import numpy as np

class PoldiMerge(PythonAlgorithm):
comparedPropertyNames = ["TablePositionX", "TablePositionY", "TablePositionZ", "ChopperSpeed"]
comparedPropertyNames = ["TablePositionX", "TablePositionY", "TablePositionZ"]
comparedInstrumentParameters = [("detector", "two_theta"),
("chopper", "t0"),
("chopper", "t0_const")]
outputWorkspaceName = None
checkInstruments = True

def category(self):
return "SINQ\\Poldi"
Expand All @@ -27,38 +31,37 @@ def PyInit(self):
direction=Direction.Output),
doc="Workspace where all counts from the list workspaces have been added")

self.declareProperty("CheckInstruments", True, "If checked, only workspaces with equal instrument parameters are merged. Do not disable without a very good reason.")

def PyExec(self):
self.checkInstruments = self.getProperty("CheckInstruments").value

workspaceNames = self.getProperty("WorkspaceNames").value
self.outputWorkspaceName = self.getProperty("OutputWorkspace").valueAsStr

self.log().information("Workspaces to merge: %i" % (len(workspaceNames)))

if False in [AnalysisDataService.doesExist(x) for x in workspaceNames]:
raise KeyError("Not all strings in the input list are valid workspace names.")

workspaces = [AnalysisDataService.retrieve(x) for x in workspaceNames]
workspaces = []

# Create a target workspace for the summation. It inherits the log of
# the first workspace used in the summation.
output = WorkspaceFactory.create(workspaces[0])
for wsName in workspaceNames:
if not AnalysisDataService.doesExist(wsName):
raise KeyError("Not all strings in the input list are valid workspace names.")

xdata = workspaces[0].dataX(0)
ydata = np.zeros(len(xdata))
ws = AnalysisDataService.retrieve(wsName)
workspaces += self.getWorkspacesRecursive(ws)

for h in range(output.getNumberHistograms()):
output.setX(h, xdata)
output.setY(h, ydata)

AnalysisDataService.addOrReplace(self.outputWorkspaceName, output)
workspaceCount = len(workspaces)

while workspaces:
current = workspaces.pop(0)
for i in range(workspaceCount):
currentWorkspace = workspaces[i]
for j in range(i + 1, workspaceCount):
try:
self.canMerge(currentWorkspace, workspaces[j])
except RuntimeError as error:
self.handleError(error)

try:
if self.canMerge(output, current):
output += current
except RuntimeError as error:
self.handleError(error)
output = MergeRuns(workspaceNames)

self.setProperty("OutputWorkspace", output)

Expand All @@ -69,17 +72,54 @@ def canMerge(self, leftWorkspace, rightWorkspace):
leftRun = leftWorkspace.getRun()
rightRun = rightWorkspace.getRun()

return self.propertiesMatch(leftRun, rightRun)
if not self.chopperSpeedsMatch(leftRun, rightRun):
raise RuntimeError("Chopper speeds do not match (" + '&'.join((leftWorkspace.getName(), rightWorkspace.getName())) + ")")

return self.propertiesMatch(leftRun, rightRun) and self.instrumentsMatch(leftWorkspace, rightWorkspace)

def timingsMatch(self, leftXData, rightXData):
leftDeltaX = leftXData[1] - leftXData[0]
rightDeltaX = rightXData[1] - rightXData[0]

return abs(leftDeltaX - rightDeltaX) < 1e-4 and abs(rightXData[0] - leftXData[0]) < 1e-4

def chopperSpeedsMatch(self, leftRun, rightRun):
chopperSpeedLeft = self.makePlausibleChopperSpeed(self.getPropertyValue(leftRun.getProperty("ChopperSpeed")))
chopperSpeedRight = self.makePlausibleChopperSpeed(self.getPropertyValue(rightRun.getProperty("ChopperSpeed")))

return abs(chopperSpeedLeft - chopperSpeedRight) < 1e-4

def makePlausibleChopperSpeed(self, chopperSpeed):
# This is related to ticket #10090, where a new field in new data is used
# when that ticket is finished, new data files will not need this
# cleanup method anymore.
return np.floor((chopperSpeed + 250.0) / 500.0) * 500.0;

def instrumentsMatch(self, leftWorkspace, rightWorkspace):
leftInstrument = leftWorkspace.getInstrument()
rightInstrument = rightWorkspace.getInstrument()

return (not self.checkInstruments) or self.instrumentParametersMatch(leftInstrument, rightInstrument)

def instrumentParametersMatch(self, leftInstrument, rightInstrument):
if not (leftInstrument.getDetector(0).getPos() == rightInstrument.getDetector(0).getPos()):
raise RuntimeError("Detector positions are not equal")

for parameterTuple in self.comparedInstrumentParameters:
leftValue = self.getParameterValue(leftInstrument, parameterTuple)
rightValue = self.getParameterValue(rightInstrument, parameterTuple)

if abs(leftValue - rightValue) > 1e-12:
raise RuntimeError("Instrument parameter '%s'/'%s' does not match" % parameterTuple)

return True;

def getParameterValue(self, instrument, parameterTuple):
return instrument.getComponentByName(parameterTuple[0]).getNumberParameter(parameterTuple[1])[0]

def propertiesMatch(self, leftRun, rightRun):
for propertyName in self.comparedPropertyNames:
if abs(self.getPropertyValue(leftRun.getProperty(propertyName)) - self.getPropertyValue(rightRun.getProperty(propertyName))) > 1e-4:
if abs(self.getPropertyValue(leftRun.getProperty(propertyName)) - self.getPropertyValue(rightRun.getProperty(propertyName))) > 5e-3:
raise RuntimeError("Property '%s' does not match" % (propertyName))

return True
Expand All @@ -96,4 +136,18 @@ def handleError(self, error):

raise RuntimeError("Workspaces can not be merged. %s. Aborting." % (str(error)))

def getWorkspacesRecursive(self, workspace):
returnList = []
if isinstance(workspace, WorkspaceGroup):
for i in range(workspace.getNumberOfEntries()):
returnList += self.getWorkspacesRecursive(workspace.getItem(i))

elif isinstance(workspace, MatrixWorkspace):
returnList.append(workspace)

else:
raise RuntimeError("Can only merge MatrixWorkspaces, this is " + type(workspace))

return returnList

AlgorithmFactory.subscribe(PoldiMerge)
Expand Up @@ -25,6 +25,8 @@ def __init__(self, *args):
self.badTimingOffset = CreateWorkspace(rightDataBadOffset, ydata, OutputWorkspace="BadTimingOffset")
self.badTimingDelta = CreateWorkspace(rightDataBadDelta, ydata, OutputWorkspace="BadTimingDelta")

self.groupGood = GroupWorkspaces(["Base", "GoodTiming"], OutputWorkspace="GoodGroup")

goodProperty = 10.0
badProperty = 20.0

Expand All @@ -37,7 +39,7 @@ def __init__(self, *args):
self.goodTimingBadProperties.getRun().addProperty(p, badProperty, True)

def __runMerge__(self, workspaceNames):
return PoldiMerge(WorkspaceNames=workspaceNames, OutputWorkspace="PoldiMergeOutput")
return PoldiMerge(WorkspaceNames=workspaceNames, OutputWorkspace="PoldiMergeOutput", CheckInstruments=False)

def test_happyCase(self):
output = self.__runMerge__("Base,GoodTiming")
Expand All @@ -56,6 +58,23 @@ def test_happyCase(self):

DeleteWorkspace("PoldiMergeOutput")

def test_workspaceGroup(self):
output = self.__runMerge__("GoodGroup")

self.assertTrue(isinstance(output, MatrixWorkspace))

dataX = output.dataX(0)
self.assertEqual(dataX[0], 0.0)
self.assertEqual(dataX[-1], 3.0)
self.assertEqual(len(dataX), 4)

dataY = output.dataY(0)
self.assertEqual(dataY[0], 2.0)
self.assertEqual(dataY[1], 2.0)
self.assertEqual(len(dataY), 4)

DeleteWorkspace("PoldiMergeOutput")

def test_timingDelta(self):
self.assertRaises(RuntimeError, lambda: self.__runMerge__("Base,BadTimingDelta"))
self.assertFalse(AnalysisDataService.doesExist("PoldiMergeOutput"))
Expand Down

0 comments on commit 79ccf37

Please sign in to comment.