diff --git a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/CutMD.py b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/CutMD.py index e0dd092feee8fc07980bd350384890055324fd32..1da69c39512bc782740abaabcf315e96bf19ec32 100644 --- a/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/CutMD.py +++ b/Code/Mantid/Framework/PythonInterface/plugins/algorithms/WorkflowAlgorithms/CutMD.py @@ -49,45 +49,53 @@ class CutMD(DataProcessorAlgorithm): self.declareProperty(name="CheckAxes", defaultValue=True, doc="Check that the axis look to be correct, and abort if not.") + + def __calculate_steps(self, extents, horace_binning ): + # Because the step calculations may involve moving the extents, we re-publish the extents. + out_extents = extents + out_n_bins = list() + for i in range(len(horace_binning)): + + n_arguments = len(horace_binning[i]) + max_extent_index = (i*2) + 1 + min_extent_index = (i*2) + dim_range = extents[ max_extent_index ] - extents[ min_extent_index ] + + if n_arguments == 0: + raise ValueError("binning parameter cannot be empty") + elif n_arguments == 1: + step_size = horace_binning[i][0] + if step_size > dim_range: + step_size = dim_range + n_bins = int( dim_range / step_size) + # Correct the max extent to fit n * step_size + out_extents[max_extent_index] = extents[min_extent_index] + ( n_bins * step_size ) + elif n_arguments == 2: + out_extents[ min_extent_index ] = horace_binning[i][0] + out_extents[ max_extent_index ] = horace_binning[i][1] + n_bins = 1 + elif n_arguments == 3: + dim_min = horace_binning[i][0] + dim_max = horace_binning[i][2] + step_size = horace_binning[i][1] + dim_range = dim_max - dim_min + if step_size > dim_range: + step_size = dim_range + n_bins = int( dim_range / step_size) + # Correct the max extent to fit n * step_size + out_extents[ max_extent_index ] = dim_min + ( n_bins * step_size ) + out_extents[ min_extent_index ] = dim_min + if n_bins < 1: + raise ValueError("Number of bins calculated to be < 1") + out_n_bins.append( n_bins ) + return out_extents, out_n_bins - def __to_mantid_slicing_binning(self, horace_binning, to_cut, dimension_index): + def __extents_in_current_projection(self, to_cut, dimension_index): dim = to_cut.getDimension(dimension_index) dim_min = dim.getMinimum() dim_max = dim.getMaximum() - dim_range = dim_max - dim_min - n_arguments = len(horace_binning) - if n_arguments == 0: - raise ValueError("binning parameter cannot be empty") - elif n_arguments == 1: - step_size = horace_binning[0] - if step_size > dim_range: - step_size = dim_range - n_bins = int( dim_range / step_size) - # Calculate the maximum based on step size and number of bins - dim_max = dim_min + ( n_bins * step_size ) - elif n_arguments == 2: - dim_min = horace_binning[0] - dim_max = horace_binning[1] - n_bins = 1 - elif n_arguments == 3: - dim_min = horace_binning[0] - dim_max = horace_binning[2] - step_size = horace_binning[1] - dim_range = dim_max - dim_min - if step_size > dim_range: - step_size = dim_range - n_bins = int( dim_range / step_size) - dim_max = dim_min + ( n_bins * step_size ) - #if dim_max != horace_binning[2]: - # pass # TODO, we should generate a warning at this point. - else: - raise ValueError("Too many arguments given to the binning parameter") - if dim_min >= dim_max: - raise ValueError("Dimension Min >= Max value. Min %.2f Max %.2f", min, max) - if n_bins < 1: - raise ValueError("Number of bins calculated to be < 1") - return (dim_min, dim_max, n_bins) + return (dim_min, dim_max) def __calculate_extents(self, v, u, w, limits): M=np.array([u,v,w]) @@ -114,7 +122,7 @@ class CutMD(DataProcessorAlgorithm): extents.append(np.amax(new_coords[:,i])) return extents - + def __uvw_from_projection_table(self, projection_table): if not isinstance(projection_table, ITableWorkspace): I = np.identity(3) @@ -193,6 +201,29 @@ class CutMD(DataProcessorAlgorithm): raise ValueError("Projection table schema is wrong! Column names received: " + str(column_names) ) if projection_table.rowCount() != 3: raise ValueError("Projection table expects 3 rows") + + + def __scale_projection(self, (u, v, w), origin_units, target_units, to_cut): + + if set(origin_units) == set(target_units): + return (u,v,w) # Nothing to do. + + ol = to_cut.getExperimentInfo(0).run().sample().getOrientedLattice() + + d_star_w = 2 * np.pi * ol.dstar(u, v, w) + projection_scaled = [u, v, w] + + to_from_pairs = zip(origin_units, target_units) + for i in range(len(to_from_pairs)) : + from_unit, to_unit = to_from_pairs[i] + if from_unit == to_unit: + continue + elif from_unit == ProjectionUnit.a: # From inverse Angstroms to rlu + projection_scaled[i] *= d_star_w + else: # From rlu to inverse Anstroms + projection_scaled[i] /= d_star_w + return projection_scaled + def PyExec(self): to_cut = self.getProperty("InputWorkspace").value @@ -209,26 +240,30 @@ class CutMD(DataProcessorAlgorithm): p3_bins = self.getProperty("P3Bin").value p4_bins = self.getProperty("P4Bin").value - xbins = self.__to_mantid_slicing_binning(p1_bins, to_cut, 0); - ybins = self.__to_mantid_slicing_binning(p2_bins, to_cut, 1); - zbins = self.__to_mantid_slicing_binning(p3_bins, to_cut, 2); - bins = [ int(xbins[2]), int(ybins[2]), int(zbins[2]) ] + x_extents = self.__extents_in_current_projection(to_cut, 0); + y_extents = self.__extents_in_current_projection(to_cut, 1); + z_extents = self.__extents_in_current_projection(to_cut, 2); + + projection = self.__uvw_from_projection_table(projection_table) + target_units = self.__units_from_projection_table(projection_table) + origin_units = (ProjectionUnit.r, ProjectionUnit.r, ProjectionUnit.r) # TODO. This is a hack! + + u,v,w = self.__scale_projection(projection, origin_units, target_units, to_cut) + + extents = self.__calculate_extents(v, u, w, ( x_extents, y_extents, z_extents ) ) + extents, bins = self.__calculate_steps( extents, ( p1_bins, p2_bins, p3_bins ) ) if p4_bins: if (ndims == 4): ebins = self.__to_mantid_slicing_binning(p1_bins, to_cut, 3); + e_units = to_cut.getDimension(3).getUnits() bins.append(int(ebins[2])) + target_units.append(e_units) else: raise ValueError("Cannot specify P4Bins unless the workspace is of sufficient dimensions") - projection = self.__uvw_from_projection_table(projection_table) - units = self.__units_from_projection_table(projection_table) - u,v,w = projection - - # Calculate the extents based on the bin limits only. - extents = self.__calculate_extents(v, u, w, ( (xbins[0], xbins[1]), (ybins[0], ybins[1]), (zbins[0], zbins[1]))) - projection_labels = self.__make_labels(projection) + ''' Actually perform the binning operation ''' @@ -244,16 +279,19 @@ class CutMD(DataProcessorAlgorithm): for i in range(0, to_cut.getNumDims()): if i <= 2: label = projection_labels[i] - unit = "TODO" # Haven't figured out how to do this yet. + unit = target_units[i] vec = projection[i] value = "%s, %s, %s" % ( label, unit, ",".join(map(str, vec))) cut_alg.setPropertyValue("BasisVector{0}".format(i) , value) if i > 2: - raise RuntimeError("Not implmented yet for non-crystallographic basis vector generation.") + raise RuntimeError("Not implemented yet for non-crystallographic basis vector generation.") cut_alg.setProperty("OutputExtents", extents) cut_alg.setProperty("OutputBins", bins) cut_alg.execute() + + # TODO. The projection matrix should be written to the output workspace at this point. + slice = cut_alg.getProperty("OutputWorkspace").value self.setProperty("OutputWorkspace", slice) diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/CutMDTest.py b/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/CutMDTest.py index 48e2d790658e3de14f8d23bb58a2c0964374b2f4..ad313d5abd44ab4c926e0187990e6142a8c8d096 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/CutMDTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/CutMDTest.py @@ -13,6 +13,8 @@ class CutMDTest(unittest.TestCase): data_ws = CreateMDWorkspace(Dimensions=3, Extents=[-10,10,-10,10,-10,10], Names="A,B,C", Units="U,U,U") # Mark the workspace as being in HKL SetSpecialCoordinates(InputWorkspace=data_ws, SpecialCoordinates='HKL') + # Set the UB + SetUB(Workspace=data_ws, a = 1, b = 1, c = 1, alpha =90, beta=90, gamma = 90) # Add some data to the workspace FakeMDEventData(InputWorkspace=data_ws, PeakParams=[10000,0,0,0,1]) self.__in_md = data_ws @@ -86,6 +88,8 @@ class CutMDTest(unittest.TestCase): def test_orthogonal_slice_with_scaling(self): # We create a fake workspace and check to see that the extents get scaled with the new coordinate system when sliced to_cut = CreateMDWorkspace(Dimensions=3, Extents=[-1,1,-1,1,-1,1], Names='H,K,L', Units='U,U,U') + # Set the UB + SetUB(Workspace=to_cut, a = 1, b = 1, c = 1, alpha =90, beta=90, gamma = 90) SetSpecialCoordinates(InputWorkspace=to_cut, SpecialCoordinates='HKL') @@ -97,9 +101,9 @@ class CutMDTest(unittest.TestCase): projection.addColumn("double", "u") projection.addColumn("double", "v") projection.addColumn("str", "type") - projection.addRow([scale_x,0,"aaa"]) - projection.addRow([0,scale_y,"aaa"]) - projection.addRow([0,0,"aaa"]) + projection.addRow([scale_x,0,"r"]) + projection.addRow([0,scale_y,"r"]) + projection.addRow([0,0,"r"]) out_md = CutMD(to_cut, Projection=projection, P1Bin=[0.1], P2Bin=[0.1], P3Bin=[0.1]) @@ -122,7 +126,8 @@ class CutMDTest(unittest.TestCase): def test_non_orthogonal_slice(self): # We create a fake workspace and check to see that the extents get transformed to the new coordinate system. to_cut = CreateMDWorkspace(Dimensions=3, Extents=[-1,1,-1,1,-1,1], Names='H,K,L', Units='U,U,U') - + # Set the UB + SetUB(Workspace=to_cut, a = 1, b = 1, c = 1, alpha =90, beta=90, gamma = 90) SetSpecialCoordinates(InputWorkspace=to_cut, SpecialCoordinates='HKL') projection = CreateEmptyTableWorkspace() @@ -132,9 +137,9 @@ class CutMDTest(unittest.TestCase): projection.addColumn("double", "w") projection.addColumn("double", "offsets") projection.addColumn("str", "type") - projection.addRow([1,-1, 0, 0, "aaa"]) - projection.addRow([1, 1, 0, 0, "aaa"]) - projection.addRow([0, 0, 1, 0, "aaa"]) + projection.addRow([1,-1, 0, 0, "r"]) + projection.addRow([1, 1, 0, 0, "r"]) + projection.addRow([0, 0, 1, 0, "r"]) out_md = CutMD(to_cut, Projection=projection, P1Bin=[0.1], P2Bin=[0.1], P3Bin=[0.1], NoPix=True) @@ -157,7 +162,8 @@ class CutMDTest(unittest.TestCase): def test_orthogonal_slice_with_cropping(self): # We create a fake workspace and check to see that using bin inputs for cropping works to_cut = CreateMDWorkspace(Dimensions=3, Extents=[-1,1,-1,1,-1,1], Names='H,K,L', Units='U,U,U') - + # Set the UB + SetUB(Workspace=to_cut, a = 1, b = 1, c = 1, alpha =90, beta=90, gamma = 90) SetSpecialCoordinates(InputWorkspace=to_cut, SpecialCoordinates='HKL') projection = CreateEmptyTableWorkspace() @@ -167,9 +173,9 @@ class CutMDTest(unittest.TestCase): projection.addColumn("double", "w") projection.addColumn("double", "offsets") projection.addColumn("str", "type") - projection.addRow([1, 0, 0, 0, "aaa"]) - projection.addRow([0, 1, 0, 0, "aaa"]) - projection.addRow([0, 0, 1, 0, "aaa"]) + projection.addRow([1, 0, 0, 0, "r"]) + projection.addRow([0, 1, 0, 0, "r"]) + projection.addRow([0, 0, 1, 0, "r"]) ''' Specify the cropping boundaries as part of the bin inputs.