def test_that_create_manage_non_child_algorithm_is_called_once_in_centre_finder_new( self, make_algorithm_mock, load_data_mock): r_min = 5 r_max = 10 position_1_start = 300 position_2_start = -300 tolerance = 0.001 find_direction = FindDirectionEnum.All iterations = 10 verbose = False load_data_mock.return_value = { SANSDataType.SampleScatter: [mock.MagicMock()] }, { SANSDataType.SampleScatter: [mock.MagicMock()] } beam_centre_finder = "SANSBeamCentreFinder" beam_centre_finder_options = { "Component": 'LAB', "Iterations": iterations, "RMin": r_min / 1000, "RMax": r_max / 1000, "Position1Start": position_1_start, "Position2Start": position_2_start, "Tolerance": tolerance, "Direction": FindDirectionEnum.to_string(find_direction), "Verbose": verbose } centre_finder_new(self.state, r_min=r_min, r_max=r_max, iterations=iterations, position_1_start=position_1_start, position_2_start=position_2_start, tolerance=tolerance, find_direction=find_direction, verbose=verbose, component=DetectorType.LAB) make_algorithm_mock.assert_called_once_with( beam_centre_finder, **beam_centre_finder_options)
def test_that_create_manage_non_child_algorithm_is_called_once_in_centre_finder_new(self, make_algorithm_mock, load_data_mock): r_min = 5 r_max = 10 position_1_start = 300 position_2_start = -300 tolerance = 0.001 find_direction = FindDirectionEnum.All iterations = 10 verbose = False load_data_mock.return_value = {SANSDataType.SampleScatter: [mock.MagicMock()]}, { SANSDataType.SampleScatter: [mock.MagicMock()]} beam_centre_finder = "SANSBeamCentreFinder" beam_centre_finder_options = {"Component":'LAB', "Iterations": iterations, "RMin": r_min / 1000, "RMax": r_max / 1000, "Position1Start": position_1_start, "Position2Start": position_2_start, "Tolerance": tolerance, "Direction": FindDirectionEnum.to_string(find_direction), "Verbose": verbose} centre_finder_new(self.state, r_min=r_min, r_max=r_max, iterations=iterations, position_1_start=position_1_start ,position_2_start=position_2_start, tolerance=tolerance, find_direction=find_direction ,verbose=verbose, component=DetectorType.LAB) make_algorithm_mock.assert_called_once_with(beam_centre_finder, **beam_centre_finder_options)
def centre_finder_new(state, r_min = 0.06, r_max = 0.26, iterations = 10, position_1_start = 0.0, position_2_start = 0.0 , tolerance = 0.0001251, find_direction = FindDirectionEnum.All, verbose=False, component=DetectorType.LAB): """ Finds the beam centre from a good initial guess. This function finds the centre of the beam by splitting the workspace up into 4 quadrants and running a reduction on each. The (left, right) and (up, down) reductions are then compared producing residuals which are minimised through repeated iteration. :param state: This is a sans state, to find the beam centre for. :param r_min: This is the inner radius of the quartile mask. :param r_max: This is the outer radius of the quartile mask. :param max_iter: This is the maximum number of iterations. :param position_1_start: This is the starting position of the search on the x axis. :param position_2_start: This is the starting position of the search on the y axis. :param tolerance: This is the tolerance for the search. :param find_direction: This is an enumerator controlling which axis or both should be searched. """ # ------------------------------------------------------------------------------------------------------------------ # Load the data # ------------------------------------------------------------------------------------------------------------------ workspace_to_name = {SANSDataType.SampleScatter: "SampleScatterWorkspace", SANSDataType.SampleTransmission: "SampleTransmissionWorkspace", SANSDataType.SampleDirect: "SampleDirectWorkspace", SANSDataType.CanScatter: "CanScatterWorkspace", SANSDataType.CanTransmission: "CanTransmissionWorkspace", SANSDataType.CanDirect: "CanDirectWorkspace"} workspace_to_monitor = {SANSDataType.SampleScatter: "SampleScatterMonitorWorkSpace", SANSDataType.CanScatter: "CanScatterMonitorWorkspace"} workspaces, monitors = provide_loaded_data(state, False, workspace_to_name, workspace_to_monitor) # ------------------------------------------------------------------------------------------------------------------ # Get reduction settings # Split into individual bundles which can be reduced individually. We split here if we have multiple periods or # sliced times for example. For the beam centre finder we only use the first period. # ------------------------------------------------------------------------------------------------------------------ reduction_packages = get_reduction_packages(state, workspaces, monitors) reduction_package = reduction_packages[0] # ------------------------------------------------------------------------------------------------------------------ # Setup the beam centre finder algorithm. # ------------------------------------------------------------------------------------------------------------------ beam_centre_finder = "SANSBeamCentreFinder" beam_centre_finder_options = {"Iterations": iterations, "RMin": r_min/1000, "RMax": r_max/1000, "Position1Start": position_1_start, "Position2Start": position_2_start, "Tolerance": tolerance, "Direction" : FindDirectionEnum.to_string(find_direction), "Verbose": verbose, "Component": DetectorType.to_string(component)} beam_centre_alg = create_managed_non_child_algorithm(beam_centre_finder, **beam_centre_finder_options) beam_centre_alg.setChild(False) set_properties_for_beam_centre_algorithm(beam_centre_alg, reduction_package, workspace_to_name, workspace_to_monitor) # ----------------------------------- # Run the beam centre finder algorithm. # ----------------------------------- beam_centre_alg.execute() # ----------------------------------- # Get the outputs # ----------------------------------- centre1 = beam_centre_alg.getProperty("Centre1").value centre2 = beam_centre_alg.getProperty("Centre2").value return {"pos1": centre1, "pos2": centre2}
def centre_finder_new(state, r_min=0.06, r_max=0.26, iterations=10, position_1_start=0.0, position_2_start=0.0, tolerance=0.0001251, find_direction=FindDirectionEnum.All): """ Finds the beam centre from a good initial guess. This function finds the centre of the beam by splitting the workspace up into 4 quadrants and running a reduction on each. The (left, right) and (up, down) reductions are then compared producing residuals which are minimised through repeated iteration. :param state: This is a sans state, to find the beam centre for. :param r_min: This is the inner radius of the quartile mask. :param r_max: This is the outer radius of the quartile mask. :param max_iter: This is the maximum number of iterations. :param position_1_start: This is the starting position of the search on the x axis. :param position_2_start: This is the starting position of the search on the y axis. :param tolerance: This is the tolerance for the search. :param fine_direction: This is an enumerator controlling which axis or both should be searched. """ # ------------------------------------------------------------------------------------------------------------------ # Load the data # ------------------------------------------------------------------------------------------------------------------ workspace_to_name = { SANSDataType.SampleScatter: "SampleScatterWorkspace", SANSDataType.SampleTransmission: "SampleTransmissionWorkspace", SANSDataType.SampleDirect: "SampleDirectWorkspace", SANSDataType.CanScatter: "CanScatterWorkspace", SANSDataType.CanTransmission: "CanTransmissionWorkspace", SANSDataType.CanDirect: "CanDirectWorkspace" } workspace_to_monitor = { SANSDataType.SampleScatter: "SampleScatterMonitorWorkSpace", SANSDataType.CanScatter: "CanScatterMonitorWorkspace" } workspaces, monitors = provide_loaded_data(state, False, workspace_to_name, workspace_to_monitor) # ------------------------------------------------------------------------------------------------------------------ # Get reduction settings # Split into individual bundles which can be reduced individually. We split here if we have multiple periods or # sliced times for example. For the beam centre finder we only use the first period. # ------------------------------------------------------------------------------------------------------------------ reduction_packages = get_reduction_packages(state, workspaces, monitors) reduction_package = reduction_packages[0] # ------------------------------------------------------------------------------------------------------------------ # Setup the beam centre finder algorithm. # ------------------------------------------------------------------------------------------------------------------ beam_centre_finder = "SANSBeamCentreFinder" beam_centre_finder_options = { "Iterations": iterations, "RMin": r_min / 1000, "RMax": r_max / 1000, "Position1Start": position_1_start, "Position2Start": position_2_start, "Tolerance": tolerance, "Direction": FindDirectionEnum.to_string(find_direction) } beam_centre_alg = create_managed_non_child_algorithm( beam_centre_finder, **beam_centre_finder_options) beam_centre_alg.setChild(False) set_properties_for_beam_centre_algorithm(beam_centre_alg, reduction_package, workspace_to_name, workspace_to_monitor) # ----------------------------------- # Run the beam centre finder algorithm. # ----------------------------------- beam_centre_alg.execute() # ----------------------------------- # Get the outputs # ----------------------------------- centre1 = beam_centre_alg.getProperty("Centre1").value centre2 = beam_centre_alg.getProperty("Centre2").value create_output_table(centre1, centre2) return {"pos1": centre1, "pos2": centre2}
def PyInit(self): # ---------- # INPUT # ---------- # Workspace which is to be cropped self.declareProperty( PropertyManagerProperty('SANSState'), doc='A property manager which fulfills the SANSState contract.') self.declareProperty(MatrixWorkspaceProperty( "SampleScatterWorkspace", '', optional=PropertyMode.Mandatory, direction=Direction.Input), doc='The sample scatter data') self.declareProperty(MatrixWorkspaceProperty( "SampleScatterMonitorWorkspace", '', optional=PropertyMode.Mandatory, direction=Direction.Input), doc='The sample scatter monitor data') self.declareProperty(MatrixWorkspaceProperty( "SampleTransmissionWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The sample transmission data') self.declareProperty(MatrixWorkspaceProperty( "SampleDirectWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The sample direct data') self.declareProperty(MatrixWorkspaceProperty( "CanScatterWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can scatter data') self.declareProperty(MatrixWorkspaceProperty( "CanScatterMonitorWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can scatter monitor data') self.declareProperty(MatrixWorkspaceProperty( "CanTransmissionWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can transmission data') self.declareProperty(MatrixWorkspaceProperty( "CanDirectWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can direct data') # The component, i.e. HAB or LAB allowed_detectors = StringListValidator([ DetectorType.to_string(DetectorType.LAB), DetectorType.to_string(DetectorType.HAB) ]) self.declareProperty( "Component", DetectorType.to_string(DetectorType.LAB), validator=allowed_detectors, direction=Direction.Input, doc="The component of the instrument which is to be reduced.") self.declareProperty("Iterations", 10, direction=Direction.Input, doc="The maximum number of iterations.") self.declareProperty("RMin", 0.6, direction=Direction.Input, doc="The inner radius of the quartile mask") self.declareProperty('RMax', 0.28, direction=Direction.Input, doc="The outer radius of the quartile mask") self.declareProperty('Position1Start', 0.0, direction=Direction.Input, doc="The search start position1") self.declareProperty('Position2Start', 0.0, direction=Direction.Input, doc="The search start position2") self.declareProperty('Tolerance', 0.0001251, direction=Direction.Input, doc="The search tolerance") self.declareProperty( 'Direction', FindDirectionEnum.to_string(FindDirectionEnum.All), direction=Direction.Input, doc= "The search direction is an enumerable which can be either All, LeftRight or UpDown" ) self.declareProperty( 'Verbose', False, direction=Direction.Input, doc="Whether to keep workspaces from each iteration in ADS.") # ---------- # Output # ---------- # Workspace which is to be cropped self.declareProperty( 'Centre1', 0.0, direction=Direction.Output, doc="The centre position found in the first dimension") self.declareProperty( 'Centre2', 0.0, direction=Direction.Output, doc="The centre position found in the second dimension")
def PyExec(self): state = self._get_state() state_serialized = state.property_manager logger = Logger("CentreFinder") logger.notice("Starting centre finder routine...") progress = self._get_progress() self.scale_1 = 1000 self.scale_2 = 1000 verbose = self.getProperty('Verbose').value x_start = self.getProperty("Position1Start").value y_start = self.getProperty("Position2Start").value sample_scatter = self._get_cloned_workspace("SampleScatterWorkspace") sample_scatter_monitor = self._get_cloned_workspace( "SampleScatterMonitorWorkspace") sample_transmission = self._get_cloned_workspace( "SampleTransmissionWorkspace") sample_direct = self._get_cloned_workspace("SampleDirectWorkspace") instrument = sample_scatter.getInstrument() if instrument.getName() == 'LARMOR': self.scale_1 = 1.0 can_scatter = self._get_cloned_workspace("CanScatterWorkspace") can_scatter_monitor = self._get_cloned_workspace( "CanScatterMonitorWorkspace") can_transmission = self._get_cloned_workspace( "CanTransmissionWorkspace") can_direct = self._get_cloned_workspace("CanDirectWorkspace") component = self.getProperty("Component").value tolerance = self.getProperty("Tolerance").value max_iterations = self.getProperty("Iterations").value r_min = self.getProperty("RMin").value r_max = self.getProperty("RMax").value instrument_file = get_instrument_paths_for_sans_file( state.data.sample_scatter) position_1_step = get_named_elements_from_ipf_file( instrument_file[1], "centre-finder-step-size", float)['centre-finder-step-size'] try: position_2_step = get_named_elements_from_ipf_file( instrument_file[1], "centre-finder-step-size2", float)['centre-finder-step-size2'] except: position_2_step = position_1_step find_direction = self.getProperty("Direction").value if find_direction == FindDirectionEnum.to_string( FindDirectionEnum.Left_Right): position_2_step = 0.0 elif find_direction == FindDirectionEnum.to_string( FindDirectionEnum.Up_Down): position_1_step = 0.0 centre1 = x_start centre2 = y_start residueLR = [] residueTB = [] centre_1_hold = x_start centre_2_hold = y_start for j in range(0, max_iterations + 1): if (j != 0): centre1 += position_1_step centre2 += position_2_step progress.report("Reducing ... Pos1 " + str(centre1) + " Pos2 " + str(centre2)) sample_quartiles = self._run_quartile_reduction( sample_scatter, sample_transmission, sample_direct, "Sample", sample_scatter_monitor, component, state_serialized, centre1, centre2, r_min, r_max) if can_scatter: can_quartiles = self._run_quartile_reduction( can_scatter, can_transmission, can_direct, "Can", can_scatter_monitor, component, state_serialized, centre1, centre2, r_min, r_max) for key in sample_quartiles: sample_quartiles[key] = perform_can_subtraction( sample_quartiles[key], can_quartiles[key], self) if mantidplot: output_workspaces = self._publish_to_ADS(sample_quartiles) if verbose: self._rename_and_group_workspaces(j, output_workspaces) residueLR.append( self._calculate_residuals( sample_quartiles[MaskingQuadrant.Left], sample_quartiles[MaskingQuadrant.Right])) residueTB.append( self._calculate_residuals( sample_quartiles[MaskingQuadrant.Top], sample_quartiles[MaskingQuadrant.Bottom])) if (j == 0): logger.notice("Itr " + str(j) + ": (" + str(self.scale_1 * centre1) + ", " + str(self.scale_2 * centre2) + ") SX=" + str(residueLR[j]) + " SY=" + str(residueTB[j])) if mantidplot: self._plot_quartiles(output_workspaces, state.data.sample_scatter) else: # have we stepped across the y-axis that goes through the beam center? if residueLR[j] > residueLR[j - 1]: # yes with stepped across the middle, reverse direction and half the step size position_1_step = -position_1_step / 2 if residueTB[j] > residueTB[j - 1]: position_2_step = -position_2_step / 2 logger.notice("Itr " + str(j) + ": (" + str(self.scale_1 * centre1) + ", " + str(self.scale_2 * centre2) + ") SX=" + str(residueLR[j]) + " SY=" + str(residueTB[j])) if (residueLR[j] + residueTB[j]) < ( residueLR[j - 1] + residueTB[j - 1] ) or state.compatibility.use_compatibility_mode: centre_1_hold = centre1 centre_2_hold = centre2 if abs(position_1_step) < tolerance and abs( position_2_step) < tolerance: # this is the success criteria, we've close enough to the center logger.notice( "Converged - check if stuck in local minimum! ") break if j == max_iterations: logger.notice( "Out of iterations, new coordinates may not be the best") self.setProperty("Centre1", centre_1_hold) self.setProperty("Centre2", centre_2_hold) logger.notice("Centre coordinates updated: [{}, {}]".format( centre_1_hold * self.scale_1, centre_2_hold * self.scale_2))
def PyInit(self): # ---------- # INPUT # ---------- # Workspace which is to be cropped self.declareProperty(PropertyManagerProperty('SANSState'), doc='A property manager which fulfills the SANSState contract.') self.declareProperty(MatrixWorkspaceProperty("SampleScatterWorkspace", '', optional=PropertyMode.Mandatory, direction=Direction.Input), doc='The sample scatter data') self.declareProperty(MatrixWorkspaceProperty("SampleScatterMonitorWorkspace", '', optional=PropertyMode.Mandatory, direction=Direction.Input), doc='The sample scatter monitor data') self.declareProperty(MatrixWorkspaceProperty("SampleTransmissionWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The sample transmission data') self.declareProperty(MatrixWorkspaceProperty("SampleDirectWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The sample direct data') self.declareProperty(MatrixWorkspaceProperty("CanScatterWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can scatter data') self.declareProperty(MatrixWorkspaceProperty("CanScatterMonitorWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can scatter monitor data') self.declareProperty(MatrixWorkspaceProperty("CanTransmissionWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can transmission data') self.declareProperty(MatrixWorkspaceProperty("CanDirectWorkspace", '', optional=PropertyMode.Optional, direction=Direction.Input), doc='The can direct data') # The component, i.e. HAB or LAB allowed_detectors = StringListValidator([DetectorType.to_string(DetectorType.LAB), DetectorType.to_string(DetectorType.HAB)]) self.declareProperty("Component", DetectorType.to_string(DetectorType.LAB), validator=allowed_detectors, direction=Direction.Input, doc="The component of the instrument which is to be reduced.") self.declareProperty("Iterations", 10, direction=Direction.Input, doc="The maximum number of iterations.") self.declareProperty("RMin", 0.6, direction=Direction.Input, doc="The inner radius of the quartile mask") self.declareProperty('RMax', 0.28, direction=Direction.Input, doc="The outer radius of the quartile mask") self.declareProperty('Position1Start', 0.0, direction=Direction.Input, doc="The search start position1") self.declareProperty('Position2Start', 0.0, direction=Direction.Input, doc="The search start position2") self.declareProperty('Tolerance', 0.0001251, direction=Direction.Input, doc="The search tolerance") self.declareProperty('Direction', FindDirectionEnum.to_string(FindDirectionEnum.All), direction=Direction.Input, doc="The search direction is an enumerable which can be either All, LeftRight or UpDown") self.declareProperty('Verbose', False, direction=Direction.Input, doc="Whether to keep workspaces from each iteration in ADS.") # ---------- # Output # ---------- # Workspace which is to be cropped self.declareProperty('Centre1', 0.0, direction=Direction.Output, doc="The centre position found in the first dimension") self.declareProperty('Centre2', 0.0, direction=Direction.Output, doc="The centre position found in the second dimension")
def PyExec(self): state = self._get_state() state_serialized = state.property_manager logger = Logger("CentreFinder") logger.notice("Starting centre finder routine...") progress = self._get_progress() self.scale_1 = 1000 self.scale_2 = 1000 verbose = self.getProperty('Verbose').value x_start = self.getProperty("Position1Start").value y_start = self.getProperty("Position2Start").value sample_scatter = self._get_cloned_workspace("SampleScatterWorkspace") sample_scatter_monitor = self._get_cloned_workspace("SampleScatterMonitorWorkspace") sample_transmission = self._get_cloned_workspace("SampleTransmissionWorkspace") sample_direct = self._get_cloned_workspace("SampleDirectWorkspace") instrument = sample_scatter.getInstrument() if instrument.getName() == 'LARMOR': self.scale_1 = 1.0 can_scatter = self._get_cloned_workspace("CanScatterWorkspace") can_scatter_monitor = self._get_cloned_workspace("CanScatterMonitorWorkspace") can_transmission = self._get_cloned_workspace("CanTransmissionWorkspace") can_direct = self._get_cloned_workspace("CanDirectWorkspace") component = self.getProperty("Component").value tolerance = self.getProperty("Tolerance").value max_iterations = self.getProperty("Iterations").value r_min = self.getProperty("RMin").value r_max = self.getProperty("RMax").value instrument_file = get_instrument_paths_for_sans_file(state.data.sample_scatter) position_1_step = get_named_elements_from_ipf_file( instrument_file[1], ["centre-finder-step-size"], float)['centre-finder-step-size'] try: position_2_step = get_named_elements_from_ipf_file( instrument_file[1], ["centre-finder-step-size2"], float)['centre-finder-step-size2'] except: position_2_step = position_1_step find_direction = self.getProperty("Direction").value if find_direction == FindDirectionEnum.to_string(FindDirectionEnum.Left_Right): position_2_step = 0.0 elif find_direction == FindDirectionEnum.to_string(FindDirectionEnum.Up_Down): position_1_step = 0.0 centre1 = x_start centre2 = y_start residueLR = [] residueTB = [] centre_1_hold = x_start centre_2_hold = y_start for j in range(0, max_iterations + 1): if(j != 0): centre1 += position_1_step centre2 += position_2_step progress.report("Reducing ... Pos1 " + str(centre1) + " Pos2 " + str(centre2)) sample_quartiles = self._run_quartile_reduction(sample_scatter, sample_transmission, sample_direct, "Sample", sample_scatter_monitor, component, state_serialized, centre1, centre2, r_min, r_max) if can_scatter: can_quartiles = self._run_quartile_reduction(can_scatter, can_transmission, can_direct, "Can", can_scatter_monitor, component, state_serialized, centre1, centre2, r_min, r_max) for key in sample_quartiles: sample_quartiles[key] = perform_can_subtraction(sample_quartiles[key], can_quartiles[key], self) if mantidplot: output_workspaces = self._publish_to_ADS(sample_quartiles) if verbose: self._rename_and_group_workspaces(j, output_workspaces) residueLR.append(self._calculate_residuals(sample_quartiles[MaskingQuadrant.Left], sample_quartiles[MaskingQuadrant.Right])) residueTB.append(self._calculate_residuals(sample_quartiles[MaskingQuadrant.Top], sample_quartiles[MaskingQuadrant.Bottom])) if(j == 0): logger.notice("Itr {0}: ( {1}, {2} ) SX={3:.5g} SY={4:.5g}". format(j, self.scale_1 * centre1, self.scale_2 * centre2, residueLR[j], residueTB[j])) if mantidplot: self._plot_quartiles(output_workspaces, state.data.sample_scatter) else: # have we stepped across the y-axis that goes through the beam center? if residueLR[j] > residueLR[j-1]: # yes with stepped across the middle, reverse direction and half the step size position_1_step = - position_1_step / 2 if residueTB[j] > residueTB[j-1]: position_2_step = - position_2_step / 2 logger.notice("Itr {0}: ( {1}, {2} ) SX={3:.5g} SY={4:.5g}". format(j, self.scale_1 * centre1, self.scale_2 * centre2, residueLR[j], residueTB[j])) if (residueLR[j]+residueTB[j]) < (residueLR[j-1]+residueTB[j-1]) or state.compatibility.use_compatibility_mode: centre_1_hold = centre1 centre_2_hold = centre2 if abs(position_1_step) < tolerance and abs(position_2_step) < tolerance: # this is the success criteria, we've close enough to the center logger.notice("Converged - check if stuck in local minimum! ") break if j == max_iterations: logger.notice("Out of iterations, new coordinates may not be the best") self.setProperty("Centre1", centre_1_hold) self.setProperty("Centre2", centre_2_hold) logger.notice("Centre coordinates updated: [{}, {}]".format(centre_1_hold*self.scale_1, centre_2_hold*self.scale_2))