Skip to content

Commit

Permalink
Refs #9436. Adding merge algorithm for POLDI data
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Wedel committed May 12, 2014
2 parents 934be65 + 10eb66b commit d784ce4
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 1 deletion.
108 changes: 108 additions & 0 deletions Code/Mantid/Framework/PythonInterface/plugins/algorithms/PoldiMerge.py
@@ -0,0 +1,108 @@
"""*WIKI*
PoldiMerge takes a list of workspace names and adds the counts, resulting in a new workspace. The difference to Plus is that it performs some POLDI-specific tests
that determine whether merging those files is sensible or not. The following requirements have to be fulfilled:
* The time-binning (x-data) of all workspaces must match (offset as well as width of time bins)
* These quantities from the sample log:
** Position of the sample table (x, y and z)
** Rotation speed of the chopper
The algorithm does not perform partial summation - if any of the workspaces does not fulfill the criteria, the intermediate result is discarded.
*WIKI*"""

from mantid.kernel import StringArrayProperty, Direction
from mantid.simpleapi import *
from mantid.api import *

import numpy as np

class PoldiMerge(PythonAlgorithm):
comparedPropertyNames = ["TablePositionX", "TablePositionY", "TablePositionZ", "ChopperSpeed"]
outputWorkspaceName = None

def category(self):
return "SINQ\\Poldi"

def name(self):
return "PoldiMerge"

def PyInit(self):
self.declareProperty(StringArrayProperty(name="WorkspaceNames",
direction=Direction.Input),
doc="List of Workspace names to merge.")

self.declareProperty(WorkspaceProperty(name="OutputWorkspace",
defaultValue="MergedPoldiWorkspaces",
direction=Direction.Output),
doc="Workspace where all counts from the list workspaces have been added")

def PyExec(self):
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]

# Create a target workspace for the summation. It inherits the log of
# the first workspace used in the summation.
output = WorkspaceFactory.create(workspaces[0])

xdata = workspaces[0].dataX(0)
ydata = np.zeros(len(xdata))

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

AnalysisDataService.addOrReplace(self.outputWorkspaceName, output)

while workspaces:
current = workspaces.pop(0)

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

self.setProperty("OutputWorkspace", output)

def canMerge(self, leftWorkspace, rightWorkspace):
if not self.timingsMatch(leftWorkspace.dataX(0), rightWorkspace.dataX(0)):
raise RuntimeError("Timings don't match")

leftRun = leftWorkspace.getRun()
rightRun = rightWorkspace.getRun()

return self.propertiesMatch(leftRun, rightRun)

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 propertiesMatch(self, leftRun, rightRun):
for propertyName in self.comparedPropertyNames:
if abs(self.getPropertyValue(leftRun.getProperty(propertyName)) - self.getPropertyValue(rightRun.getProperty(propertyName))) > 1e-4:
raise RuntimeError("Property '%s' does not match" % (propertyName))

return True

def getPropertyValue(self, runProperty):
try:
return runProperty.value[0]
except:
return runProperty.value

def handleError(self, error):
if AnalysisDataService.doesExist(self.outputWorkspaceName):
AnalysisDataService.remove(self.outputWorkspaceName)

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

AlgorithmFactory.subscribe(PoldiMerge)
Expand Up @@ -40,6 +40,7 @@ set ( TEST_PY_FILES
SANSSubtractTest.py
ExportSampleLogsToCSVFileTest.py
ExportExperimentLogTest.py
PoldiMergeTest.py
)

check_tests_valid ( ${CMAKE_CURRENT_SOURCE_DIR} ${TEST_PY_FILES} )
Expand Down
@@ -0,0 +1,76 @@
import unittest

from mantid.kernel import *
from mantid.api import *
from mantid.simpleapi import *

import numpy as np

class PoldiMergeTest(unittest.TestCase):
def __init__(self, *args):
unittest.TestCase.__init__(self, *args)
properties = ["TablePositionX", "TablePositionY", "TablePositionZ", "ChopperSpeed"]

leftData = [0.0, 1.0, 2.0, 3.0]
rightDataGood = [0.0, 1.0, 2.0, 3.0]

rightDataBadOffset = [1.0, 2.0, 3.0, 4.0]
rightDataBadDelta = [0.0, 2.0, 4.0, 6.0]

ydata = np.ones(len(leftData))

self.base = CreateWorkspace(leftData, ydata, OutputWorkspace="Base")
self.goodTiming = CreateWorkspace(rightDataGood, ydata, OutputWorkspace="GoodTiming")
self.goodTimingBadProperties = CreateWorkspace(rightDataGood, ydata, OutputWorkspace="GoodTimingBadProperties")
self.badTimingOffset = CreateWorkspace(rightDataBadOffset, ydata, OutputWorkspace="BadTimingOffset")
self.badTimingDelta = CreateWorkspace(rightDataBadDelta, ydata, OutputWorkspace="BadTimingDelta")

goodProperty = 10.0
badProperty = 20.0

for p in properties:
self.base.getRun().addProperty(p, goodProperty, True)
self.goodTiming.getRun().addProperty(p, goodProperty, True)
self.badTimingOffset.getRun().addProperty(p, goodProperty, True)
self.badTimingDelta.getRun().addProperty(p, goodProperty, True)

self.goodTimingBadProperties.getRun().addProperty(p, badProperty, True)

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

def test_happyCase(self):
output = self.__runMerge__("Base,GoodTiming")

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"))

def test_timingOffset(self):
self.assertRaises(RuntimeError, lambda: self.__runMerge__("Base,BadTimingOffset"))
self.assertFalse(AnalysisDataService.doesExist("PoldiMergeOutput"))

def test_badProperties(self):
self.assertRaises(RuntimeError, lambda: self.__runMerge__("Base,GoodTimingBadProperties"))
self.assertFalse(AnalysisDataService.doesExist("PoldiMergeOutput"))

def test_badName(self):
self.assertRaises(RuntimeError, lambda: self.__runMerge__("Base,NotExisting"))
self.assertFalse(AnalysisDataService.doesExist("PoldiMergeOutput"))

if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion Code/Mantid/instrument/POLDI_Definition_ipp13.xml
Expand Up @@ -3,7 +3,7 @@
see http://www.mantidproject.org/IDF -->
<instrument xmlns="http://www.mantidproject.org/IDF/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mantidproject.org/IDF/1.0 Schema/IDFSchema.xsd"
name="POLDI" valid-from ="2013-01-01 23:59:59"
valid-to ="2013-12-31 23:59:59"
valid-to ="2013-01-02 00:00:00"
last-modified="2010-10-07 00:00:00">
<!-- modified by FD 7/10/2010 -->

Expand Down
6 changes: 6 additions & 0 deletions Code/Mantid/instrument/nexusdictionaries/poldi.dic
Expand Up @@ -51,3 +51,9 @@ ChopperPhase=/entry1/POLDI/chopper/chopper_phase
ChopperSpeed=/entry1/POLDI/chopper/rotation_speed
#
SampleName=/entry1/POLDI/name
#
start_time=/entry1/start_time
#
TablePositionX=/entry1/sample/sample_x
TablePositionY=/entry1/sample/sample_y
TablePositionZ=/entry1/sample/sample_lift

0 comments on commit d784ce4

Please sign in to comment.