diff --git a/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/DetectorFloodWeighting.py b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/DetectorFloodWeighting.py new file mode 100644 index 0000000000000000000000000000000000000000..4c3bca66611ccf2251caed99c25e0f9febe0246f --- /dev/null +++ b/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/DetectorFloodWeighting.py @@ -0,0 +1,111 @@ +from mantid.api import DataProcessorAlgorithm, AlgorithmFactory, MatrixWorkspaceProperty, WorkspaceUnitValidator + +from mantid.kernel import Direction, FloatArrayProperty, FloatArrayBoundedValidator + +import numpy as np + + +class DetectorFloodWeighting(DataProcessorAlgorithm): + + def category(self): + return 'Workflow\\SANS' + + + def summary(self): + return 'Generates a Detector flood weighting, or sensitivity workspace' + + + def PyInit(self): + + self.declareProperty(MatrixWorkspaceProperty('InputWorkspace', '', + direction=Direction.Input, validator=WorkspaceUnitValidator("Wavelength")), + doc='Flood weighting measurement') + + validator = FloatArrayBoundedValidator() + validator.setLower(0.) + self.declareProperty(FloatArrayProperty('Bands', [], direction=Direction.Input, validator=validator), + doc='Wavelength bands to use. Single pair min to max.') + + self.declareProperty(MatrixWorkspaceProperty('OutputWorkspace', '', + direction=Direction.Output), + doc='Normalized flood weighting measurement') + + + def validateInputs(self): + """ + Validates input ranges. + """ + issues = dict() + + bands = self.getProperty('Bands').value + + if not any(bands): + issues['Bands'] = 'Bands must be supplied' + + + if not len(bands)%2 == 0: + issues['Bands'] = 'Even number of Bands boundaries expected' + + if len(bands) > 2: + issues['Bands'] = 'Presently this algorithm only supports one pair of bands' + + all_limits=list() + for i in range(0, len(bands), 2): + lower = bands[i] + upper = bands[i+1] + limits = np.arange(lower, upper) + unique = set(limits) + for existing_lims in all_limits: + if unique.intersection(set(existing_lims)): + issues['Bands'] = 'Bands must not intersect' + break + + all_limits.append(limits) + if lower >= upper: + issues['Bands'] = 'Bands should form lower, upper pairs' + + return issues + + + def PyExec(self): + + in_ws = self.getProperty('InputWorkspace').value + bands = self.getProperty('Bands').value + + # Formulate bands + params = list() + for i in range(0, len(bands), 2): + lower = bands[i] + upper = bands[i+1] + step = upper - lower + params.append((lower, step, upper)) + + accumulated_output = None + rebin = self.createChildAlgorithm("Rebin") + rebin.setProperty("Params", params[0]) + rebin.setProperty("InputWorkspace", in_ws) + rebin.execute() + accumulated_output = rebin.getProperty("OutputWorkspace").value + + # Determine the max across all spectra + y_values = accumulated_output.extractY() + max = np.amax(y_values, axis=1) + + # Create a workspace from the single max value + create = self.createChildAlgorithm("CreateSingleValuedWorkspace") + create.setProperty("DataValue", max[0]) + create.execute() + max_ws = create.getProperty("OutputWorkspace").value + + # Divide each entry by max + divide = self.createChildAlgorithm("Divide") + divide.setProperty("LHSWorkspace", accumulated_output) + divide.setProperty("RHSWorkspace", max_ws) + divide.execute() + normalized = divide.getProperty("OutputWorkspace").value + + self.setProperty('OutputWorkspace', normalized) # HACK + + +# Register algorithm with Mantid +AlgorithmFactory.subscribe(DetectorFloodWeighting) \ No newline at end of file diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt b/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt index 64c61d26428868f5d92f66d5429a8fec00236de9..bb68b040a2e73b551095e18bf876be0abb92e83b 100644 --- a/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt +++ b/Framework/PythonInterface/test/python/plugins/algorithms/CMakeLists.txt @@ -11,6 +11,7 @@ set ( TEST_PY_FILES CorrectLogTimesTest.py CreateLeBailFitInputTest.py CreateMDTest.py + DetectorFloodWeightingTest.py IndirectCalibrationTest.py CreateWorkspaceTest.py CylinderPaalmanPingsCorrectionTest.py diff --git a/Framework/PythonInterface/test/python/plugins/algorithms/DetectorFloodWeightingTest.py b/Framework/PythonInterface/test/python/plugins/algorithms/DetectorFloodWeightingTest.py new file mode 100644 index 0000000000000000000000000000000000000000..64167eec36dcf9ad6c674f1a367c0180d6965fe1 --- /dev/null +++ b/Framework/PythonInterface/test/python/plugins/algorithms/DetectorFloodWeightingTest.py @@ -0,0 +1,81 @@ +import unittest +from mantid.api import AlgorithmManager + + +class DetectorFloodWeightingTest(unittest.TestCase): + + def _create_ws(self, units="TOF", signal_value=2, data_x=range(0,10), n_spec=1): + data_y=[signal_value]*(len(data_x) - 1) + alg = AlgorithmManager.create("CreateWorkspace") + alg.setChild(True) + alg.initialize() + alg.setProperty("DataX", data_x) + alg.setProperty("DataY", data_y) + alg.setProperty("NSpec", n_spec) + alg.setProperty("OutputWorkspace", "temp") + alg.setProperty("UnitX", units) + alg.execute() + return alg.getProperty("OutputWorkspace").value + + def test_init(self): + alg = AlgorithmManager.create("DetectorFloodWeighting") + alg.initialize() + self.assertTrue(alg.isInitialized()) + + def test_input_must_be_wavelength(self): + tof_ws = self._create_ws(units="TOF") + alg = AlgorithmManager.create("DetectorFloodWeighting") + alg.setChild(True) + alg.initialize() + self.assertRaises(ValueError, alg.setProperty, "InputWorkspace", tof_ws) + + def test_cannot_have_negative_wavlength_boundaries_in_bands(self): + alg = AlgorithmManager.create("DetectorFloodWeighting") + alg.setChild(True) + alg.initialize() + self.assertRaises(ValueError, alg.setProperty, "Bands", [-1,10]) + + def test_bands_must_not_overlap(self): + alg = AlgorithmManager.create("DetectorFloodWeighting") + alg.setChild(True) + alg.initialize() + signal_value = 2 + in_ws = self._create_ws(units="Wavelength", signal_value=signal_value, data_x=range(0,10,1)) + alg.setProperty("InputWorkspace", in_ws) + bands = [1,3,2,4] # Overlap! + alg.setProperty("Bands", bands) # One band + alg.setPropertyValue("OutputWorkspace", "dummy") + self.assertRaises(RuntimeError, alg.execute) + + def test_execute_single(self): + alg = AlgorithmManager.create("DetectorFloodWeighting") + alg.setChild(True) + alg.initialize() + signal_value = 2 + in_ws = self._create_ws(units="Wavelength", signal_value=signal_value, data_x=range(0,10,1)) + alg.setProperty("InputWorkspace", in_ws) + bands = [1,10] + alg.setProperty("Bands", bands) # One band + alg.setPropertyValue("OutputWorkspace", "dummy") + alg.execute() + + out_ws = alg.getProperty("OutputWorkspace").value + self.assertEqual(1, out_ws.blocksize()) + self.assertEqual("Wavelength", out_ws.getAxis(0).getUnit().unitID()) + self.assertEqual(in_ws.getNumberHistograms(), out_ws.getNumberHistograms(), msg="Number of histograms should be unchanged.") + x_axis = out_ws.readX(0) + self.assertEquals(x_axis[0], bands[0]) + self.assertEquals(x_axis[-1], bands[-1]) + print out_ws.readY(0)[0] + self.assertEquals(out_ws.readY(0)[0], 1.0) + + + + + + + + + +if __name__ == '__main__': + unittest.main() diff --git a/docs/source/algorithms/DetectorFloodWeighting-v1.rst b/docs/source/algorithms/DetectorFloodWeighting-v1.rst new file mode 100644 index 0000000000000000000000000000000000000000..a486509ad82a5a2535bd29a7d995c4db88b04bf5 --- /dev/null +++ b/docs/source/algorithms/DetectorFloodWeighting-v1.rst @@ -0,0 +1,45 @@ +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- +This algorithm is used to calculate the detector flood weighting workspace use for pixel flood corrections. It was originally developed for the ANSTO Bilby instrument. + +This algorithm crops the data over the specified wavelength region, then normalizes each spectrum to the workspace spectrum maxima. + +Usage +----- + +**Example - Simple Generation ** + +.. testcode:: DetectorFloodWeightingExample + + import numpy as np + # create histogram workspace + dataX = range(0,10) # or use dataX=range(0,10) + dataY = [1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2] # or use dataY=[1]*9 + ws = CreateWorkspace(dataX, dataY, NSpec=2, UnitX="Wavelength") + + out_ws = DetectorFloodWeighting(InputWorkspace=ws, Bands=[0,10]) + + print 'Number Histograms',out_ws.getNumberHistograms() + print 'Min X:', out_ws.readX(0)[0], 'Max X:', out_ws.readX(0)[1] + y_data = out_ws.extractY() + print 'Min Y:', np.amin(y_data), 'Max Y:', np.amax(y_data) + +Output: + +.. testoutput:: DetectorFloodWeightingExample + Number Histograms 2 + Min X: 0.0 Max X: 10.0 + Min Y: 0.5 Max Y: 1.0 + + +.. categories:: + +.. sourcelink::