def assert_call_as_expected(exp_xmin, exp_xmax, exp_start_index, exp_end_index, transpose, is_spectra): mock_ws = _create_mock_matrixworkspace(x_axis=[10, 20, 30], y_axis=[1, 2, 3, 4, 5], distribution=False, y_is_spectra=is_spectra) mock_ws.name.return_value = 'mock_ws' model = SliceViewerModel(mock_ws) slicepoint, bin_params, dimension_indices = MagicMock(), MagicMock( ), MagicMock() help_msg = model.export_roi_to_workspace(slicepoint, bin_params, ((xmin, xmax), (ymin, ymax)), transpose, dimension_indices) self.assertEqual('ROI created: mock_ws_roi', help_msg) if is_spectra: self.assertEqual(2, mock_ws.getAxis(1).indexOfValue.call_count) else: mock_ws.getAxis(1).extractValues.assert_called_once() ExtractSpectra.assert_called_once_with( InputWorkspace=mock_ws, OutputWorkspace='mock_ws_roi', XMin=exp_xmin, XMax=exp_xmax, StartWorkspaceIndex=exp_start_index, EndWorkspaceIndex=exp_end_index, EnableLogging=True) ExtractSpectra.reset_mock()
def test_model_MDE_basis_vectors_not_normalised_when_HKL(self, mock_binmd): ws = _create_mock_mdeventworkspace(ndims=3, coords=SpecialCoordinateSystem.HKL, extents=(-3, 3, -4, 4, -5, 5), names=('h', 'k', 'l'), units=('r.l.u.', 'r.l.u.', 'r.l.u.'), isq=(True, True, True)) model = SliceViewerModel(ws) mock_binmd.return_value = self.ws_MD_3D # different workspace self.assertNotEqual(model.get_ws((None, None, 0), (1, 2, 4)), ws) mock_binmd.assert_called_once_with( AxisAligned=False, NormalizeBasisVectors=False, BasisVector0='h,r.l.u.,1.0,0.0,0.0', BasisVector1='k,r.l.u.,0.0,1.0,0.0', BasisVector2='l,r.l.u.,0.0,0.0,1.0', EnableLogging=False, InputWorkspace=ws, OutputBins=[1, 2, 1], OutputExtents=[-3, 3, -4, 4, -2.0, 2.0], OutputWorkspace=ws.name() + '_svrebinned') mock_binmd.reset_mock()
def replace_workspace(self, workspace_name, workspace): """ Called when the SliceViewerADSObserver has detected that a workspace has changed @param workspace_name: the name of the workspace that has changed @param workspace: the workspace that has changed """ if not self.model.workspace_equals(workspace_name): # TODO this is a dead branch, since the ADS observer will call this if the # names are the same, but the model "workspace_equals" simply checks for the same name return try: candidate_model = SliceViewerModel(workspace) candidate_model_properties = candidate_model.get_properties() for (property, value) in candidate_model_properties.items(): if self.initial_model_properties[property] != value: raise ValueError(f"The property {property} is different on the new workspace.") # New model is OK, proceed with updating Slice Viewer self.model = candidate_model self.new_plot, self.update_plot_data = self._decide_plot_update_methods() self.refresh_view() except ValueError as err: self._close_view_with_message( f"Closing Sliceviewer as the underlying workspace was changed: {str(err)}") return
def _assert_supports_peaks_overlay(self, expectation, ws_type, ndims=2): ws = _create_mock_workspace(ws_type, coords=SpecialCoordinateSystem.QLab, has_oriented_lattice=False, ndims=ndims) model = SliceViewerModel(ws) self.assertEqual(expectation, model.can_support_peaks_overlays())
def test_init_with_valid_MDEventWorkspace(self): mock_ws = MagicMock(spec=MultipleExperimentInfos) mock_ws.name = Mock(return_value='') mock_ws.isMDHistoWorkspace = Mock(return_value=False) mock_ws.getNumDims = Mock(return_value=4) with patch.object(SliceViewerModel, "_calculate_axes_angles"): self.assertIsNotNone(SliceViewerModel(mock_ws))
def test_calculate_axes_angles_returns_none_if_nonorthogonal_transform_not_supported( self): model = SliceViewerModel( _create_mock_workspace(MatrixWorkspace, SpecialCoordinateSystem.QLab, has_oriented_lattice=False)) self.assertIsNone(model.get_axes_angles())
def test_calculate_axes_angles_uses_identity_if_W_unavailable(self): ws = _create_mock_workspace(IMDEventWorkspace, SpecialCoordinateSystem.HKL, has_oriented_lattice=True) ws.getExperimentInfo().run().get.side_effect = KeyError model = SliceViewerModel(ws) axes_angles = model.get_axes_angles() self.assertAlmostEqual(axes_angles[1, 2], np.pi / 2, delta=1e-10) for iy in range(1, 3): self.assertAlmostEqual(axes_angles[0, iy], np.pi / 2, delta=1e-10)
def __init__(self, ws, parent=None, window_flags=Qt.Window, model=None, view=None, conf=None): """ Create a presenter for controlling the slice display for a workspace :param ws: Workspace containing data to display and slice :param parent: An optional parent widget :param window_flags: An optional set of window flags :param model: A model to define slicing operations. If None uses SliceViewerModel :param view: A view to display the operations. If None uses SliceViewerView """ model: SliceViewerModel = model if model else SliceViewerModel(ws) self.view = view if view else SliceViewerView(self, Dimensions.get_dimensions_info(ws), model.can_normalize_workspace(), parent, window_flags, conf) super().__init__(ws, self.view.data_view, model) self._logger = Logger("SliceViewer") self._peaks_presenter: PeaksViewerCollectionPresenter = None self._cutviewer_presenter = None self.conf = conf # Acts as a 'time capsule' to the properties of the model at this # point in the execution. By the time the ADS observer calls self.replace_workspace, # the workspace associated with self.model has already been changed. self.initial_model_properties = model.get_properties() self._new_plot_method, self.update_plot_data = self._decide_plot_update_methods() self.view.setWindowTitle(self.model.get_title()) self.view.data_view.create_axes_orthogonal( redraw_on_zoom=not WorkspaceInfo.can_support_dynamic_rebinning(self.model.ws)) if self.model.can_normalize_workspace(): self.view.data_view.set_normalization(ws) self.view.data_view.norm_opts.currentTextChanged.connect(self.normalization_changed) if not self.model.can_support_peaks_overlays(): self.view.data_view.disable_tool_button(ToolItemText.OVERLAY_PEAKS) # check whether to enable non-orthog view # don't know whether can always assume init with display indices (0,1) - so get sliceinfo sliceinfo = self.get_sliceinfo() if not sliceinfo.can_support_nonorthogonal_axes(): self.view.data_view.disable_tool_button(ToolItemText.NONORTHOGONAL_AXES) if not self.model.can_support_non_axis_cuts(): self.view.data_view.disable_tool_button(ToolItemText.NONAXISALIGNEDCUTS) self.view.data_view.help_button.clicked.connect(self.action_open_help_window) self.refresh_view() # Start the GUI with zoom selected. self.view.data_view.activate_tool(ToolItemText.ZOOM) self.ads_observer = SliceViewerADSObserver(self.replace_workspace, self.rename_workspace, self.ADS_cleared, self.delete_workspace)
def _assert_supports_non_axis_aligned_cuts( self, expectation, ws_type, coords=SpecialCoordinateSystem.HKL, ndims=3): model = SliceViewerModel( _create_mock_workspace(ws_type, coords, has_oriented_lattice=False, ndims=ndims)) self.assertEqual(expectation, model.can_support_non_axis_cuts())
def test_calculate_axes_angles_uses_basis_vectors_even_if_WMatrix_log_available_MDHisto( self): #test MD histo ws = _create_mock_workspace(IMDHistoWorkspace, SpecialCoordinateSystem.HKL, has_oriented_lattice=True) ws.getExperimentInfo().run().get().value = [0, 1, 1, 0, 0, 1, 1, 0, 0] model = SliceViewerModel(ws) # should revert to orthogonal axes_angles = model.get_axes_angles() self.assertAlmostEqual(axes_angles[1, 2], np.pi / 2, delta=1e-10) for iy in range(1, 3): self.assertAlmostEqual(axes_angles[0, iy], np.pi / 2, delta=1e-10)
def assert_call_as_expected(mock_ws, transpose, export_type, is_spectra, is_ragged): model = SliceViewerModel(mock_ws) slicepoint, bin_params, dimension_indices = MagicMock(), MagicMock( ), MagicMock() help_msg = model.export_cuts_to_workspace( slicepoint, bin_params, ((xmin, xmax), (ymin, ymax)), transpose, dimension_indices, export_type) if export_type == 'c': if is_spectra: mock_extract_spectra.assert_called_once() if is_ragged: mock_rebin.assert_called_once() mock_sum_spectra.assert_called_once() else: if is_ragged: self.assertEqual(2, mock_rebin.call_count) else: mock_rebin.assert_called_once() self.assertEqual(1, mock_transpose.call_count) self.assertEqual(1, mock_extract_spectra.call_count) self.assertEqual( 'Cuts along X/Y created: mock_ws_cut_x & mock_ws_cut_y', help_msg) elif export_type == 'x': mock_extract_spectra.assert_called_once() if is_ragged: self.assertEqual(1, mock_rebin.call_count) if is_spectra: mock_sum_spectra.assert_called_once() else: mock_transpose.assert_not_called() self.assertEqual('Cut along X created: mock_ws_cut_x', help_msg) elif export_type == 'y': mock_extract_spectra.assert_called_once() mock_transpose.assert_called_once() if is_ragged: self.assertEqual(2, mock_rebin.call_count) else: self.assertEqual(1, mock_rebin.call_count) self.assertEqual('Cut along Y created: mock_ws_cut_y', help_msg) mock_transpose.reset_mock() mock_rebin.reset_mock() mock_extract_spectra.reset_mock() mock_sum_spectra.reset_mock()
def test_calculate_axes_angles_uses_W_if_available(self): ws = _create_mock_workspace(IMDEventWorkspace, SpecialCoordinateSystem.HKL, has_oriented_lattice=True) ws.getExperimentInfo().run().get().value = [0, 1, 1, 0, 0, 1, 1, 0, 0] model = SliceViewerModel(ws) axes_angles = model.get_axes_angles() self.assertAlmostEqual(axes_angles[1, 2], np.pi / 4, delta=1e-10) for iy in range(1, 3): self.assertAlmostEqual(axes_angles[0, iy], np.pi / 2, delta=1e-10) # test force_orthog works axes_angles = model.get_axes_angles(force_orthogonal=True) self.assertAlmostEqual(axes_angles[1, 2], np.pi / 2, delta=1e-10)
def test_calculate_axes_angles_uses_W_if_basis_vectors_unavailable_and_W_available_MDHisto( self): #test MD histo ws = _create_mock_workspace(IMDHistoWorkspace, SpecialCoordinateSystem.HKL, has_oriented_lattice=True, ndims=3) ws.getBasisVector.side_effect = lambda x: [0.0] ws.getExperimentInfo().run().get().value = [0, 1, 1, 0, 0, 1, 1, 0, 0] model = SliceViewerModel(ws) axes_angles = model.get_axes_angles() self.assertAlmostEqual(axes_angles[1, 2], np.pi / 4, delta=1e-10) for iy in range(1, 3): self.assertAlmostEqual(axes_angles[0, iy], np.pi / 2, delta=1e-10) # test force_orthog works axes_angles = model.get_axes_angles(force_orthogonal=True) self.assertAlmostEqual(axes_angles[1, 2], np.pi / 2, delta=1e-10)
def test_get_ws_mde_sets_minimum_width_on_data_limits(self, mock_binmd): model = SliceViewerModel(self.ws_MDE_3D) mock_binmd.return_value = self.ws_MD_3D xmin = -5e-8 xmax = 5e-8 self.assertNotEqual( model.get_ws((None, None, 0), (1, 2, 4), ((xmin, xmax), (-1, 1)), [0, 1, None]), self.ws_MDE_3D) call_params = dict( AxisAligned=False, BasisVector0='h,rlu,1.0,0.0,0.0', BasisVector1='k,rlu,0.0,1.0,0.0', BasisVector2='l,rlu,0.0,0.0,1.0', EnableLogging=False, InputWorkspace=self.ws_MDE_3D, OutputBins=[1, 2, 1], OutputExtents=[xmin, xmin + MIN_WIDTH, -1, 1, -2.0, 2.0], OutputWorkspace='ws_MDE_3D_svrebinned') mock_binmd.assert_called_once_with(**call_params) mock_binmd.reset_mock()
def assert_error_returned_in_help(workspace, export_type, mock_alg, err_msg): model = SliceViewerModel(workspace) slicepoint, bin_params, dimension_indices = (None, None, None), MagicMock(), [ 0, 1, None ] mock_alg.side_effect = RuntimeError(err_msg) try: if export_type == 'r': help_msg = model.export_roi_to_workspace( slicepoint, bin_params, ((1.0, 2.0), (-1, 2.0)), True, dimension_indices) else: help_msg = model.export_cuts_to_workspace( slicepoint, bin_params, ((1.0, 2.0), (-1, 2.0)), True, dimension_indices, export_type) except Exception as exc: help_msg = str(exc) mock_alg.reset_mock() self.assertTrue(err_msg in help_msg)
def test_get_ws_MDE_with_limits_uses_limits_over_dimension_extents( self, mock_binmd): model = SliceViewerModel(self.ws_MDE_3D) mock_binmd.return_value = self.ws_MD_3D self.assertNotEqual( model.get_ws((None, None, 0), (1, 2, 4), ((-2, 2), (-1, 1)), [0, 1, None]), self.ws_MDE_3D) call_params = dict(AxisAligned=False, BasisVector0='h,rlu,1.0,0.0,0.0', BasisVector1='k,rlu,0.0,1.0,0.0', BasisVector2='l,rlu,0.0,0.0,1.0', EnableLogging=False, InputWorkspace=self.ws_MDE_3D, OutputBins=[1, 2, 1], OutputExtents=[-2, 2, -1, 1, -2.0, 2.0], OutputWorkspace='ws_MDE_3D_svrebinned') mock_binmd.assert_called_once_with(**call_params) mock_binmd.reset_mock() model.get_data((None, None, 0), (1, 2, 4), [0, 1, None], ((-2, 2), (-1, 1))) mock_binmd.assert_called_once_with(**call_params)
def test_title_for_mdeventworkspace_just_contains_ws_name(self): model = SliceViewerModel(self.ws_MDE_3D) self.assertEqual('Sliceviewer - ws_MDE_3D', model.get_title())
def test_matrix_workspace_cannot_be_normalized_if_a_distribution(self): model = SliceViewerModel(self.ws2d_histo) self.assertFalse(model.can_normalize_workspace())
def _assert_supports_non_orthogonal_axes(self, expectation, ws_type, coords, has_oriented_lattice): model = SliceViewerModel( _create_mock_workspace(ws_type, coords, has_oriented_lattice)) self.assertEqual(expectation, model.can_support_nonorthogonal_axes())
def assert_call_as_expected(transpose, dimension_indices, export_type): model = SliceViewerModel(self.ws_MDE_3D) if export_type == 'r': help_msg = model.export_roi_to_workspace( slicepoint, bin_params, ((xmin, xmax), (ymin, ymax)), transpose, dimension_indices) else: help_msg = model.export_cuts_to_workspace( slicepoint, bin_params, ((xmin, xmax), (ymin, ymax)), transpose, dimension_indices, export_type) if transpose: extents = [ymin, ymax, xmin, xmax, zmin, zmax] else: extents = [xmin, xmax, ymin, ymax, zmin, zmax] common_call_params = dict(InputWorkspace=self.ws_MDE_3D, AxisAligned=False, BasisVector0='h,rlu,1.0,0.0,0.0', BasisVector1='k,rlu,0.0,1.0,0.0', BasisVector2='l,rlu,0.0,0.0,1.0', OutputExtents=extents) xcut_name, ycut_name = 'ws_MDE_3D_cut_x', 'ws_MDE_3D_cut_y' if export_type == 'r': expected_help_msg = 'ROI created: ws_MDE_3D_roi' expected_calls = [ call(**common_call_params, OutputBins=[100, 100, 1], OutputWorkspace='ws_MDE_3D_roi') ] elif export_type == 'x': expected_help_msg = f'Cut along X created: {xcut_name}' expected_bins = [1, 100, 1] if transpose else [100, 1, 1] expected_calls = [ call(**common_call_params, OutputBins=expected_bins, OutputWorkspace=xcut_name) ] elif export_type == 'y': expected_help_msg = f'Cut along Y created: {ycut_name}' expected_bins = [100, 1, 1] if transpose else [1, 100, 1] expected_calls = [ call(**common_call_params, OutputBins=expected_bins, OutputWorkspace=ycut_name) ] elif export_type == 'c': expected_help_msg = f'Cuts along X/Y created: {xcut_name} & {ycut_name}' expected_bins = [100, 1, 1] if transpose else [1, 100, 1] expected_calls = [ call(**common_call_params, OutputBins=expected_bins, OutputWorkspace=xcut_name), call(**common_call_params, OutputBins=expected_bins, OutputWorkspace=ycut_name) ] mock_binmd.assert_has_calls(expected_calls, any_order=True) if export_type == 'r': if transpose: mock_transposemd.assert_called_once() else: mock_transposemd.assert_not_called() else: if export_type == 'x': index = 1 if transpose else 0 expected_calls = [ call(InputWorkspace=xcut_name, OutputWorkspace=xcut_name, Axes=[index]) ] elif export_type == 'y': index = 0 if transpose else 1 expected_calls = [ call(InputWorkspace=ycut_name, OutputWorkspace=ycut_name, Axes=[index]) ] elif export_type == 'c': xindex = 1 if transpose else 0 yindex = 0 if transpose else 1 expected_calls = [ call(InputWorkspace=xcut_name, OutputWorkspace=xcut_name, Axes=[xindex]), call(InputWorkspace=ycut_name, OutputWorkspace=ycut_name, Axes=[yindex]) ] mock_transposemd.assert_has_calls(expected_calls, any_order=True) self.assertEqual(expected_help_msg, help_msg) mock_binmd.reset_mock() mock_transposemd.reset_mock()
def assert_call_as_expected(transpose, dimension_indices, export_type): model = SliceViewerModel(self.ws_MD_3D) if export_type == 'r': model.export_roi_to_workspace(slicepoint, bin_params, ((xmin, xmax), (ymin, ymax)), transpose, dimension_indices) else: model.export_cuts_to_workspace(slicepoint, bin_params, ((xmin, xmax), (ymin, ymax)), transpose, dimension_indices, export_type) if export_type == 'c': export_type = 'xy' # will loop over this string as 'c' performs both 'x' and 'y' for export in export_type: xbin, ybin = [xmin, xmax], [ ymin, ymax ] # create in loop as these are altered in case of both cuts # perform transpose on limits - i.e map x/y on plot to basis of MD workspace p1/p2 if not transpose: p1_bin, p2_bin = xbin, ybin else: p2_bin, p1_bin = xbin, ybin # determine which axis was binnned if export == 'x': xbin.insert( 1, 0.0 ) # insert 0 between min,max - this means preserve binning along this dim out_name = 'ws_MD_3D_cut_x' transpose_axes = [1 if transpose else 0 ] # Axes argument of TransposeMD elif export == 'y': ybin.insert(1, 0.0) out_name = 'ws_MD_3D_cut_y' # check call to transposeMD transpose_axes = [0 if transpose else 1] else: # export == 'r' xbin.insert(1, 0.0) ybin.insert(1, 0.0) out_name = 'ws_MD_3D_roi' transpose_axes = [1, 0] if transpose else None # check call to IntegrateMDHistoWorkspace mock_intMD.assert_has_calls([ call(InputWorkspace=self.ws_MD_3D, P1Bin=p1_bin, P2Bin=p2_bin, P3Bin=[ slicepoint[2] - bin_params[2] / 2, slicepoint[2] + bin_params[2] / 2 ], OutputWorkspace=out_name) ], any_order=False) if transpose_axes is not None: mock_transposemd.assert_has_calls([ call(InputWorkspace=out_name, OutputWorkspace=out_name, Axes=transpose_axes) ], any_order=False) else: mock_transposemd.assert_not_called( ) # ROI with Transpose == False mock_intMD.reset_mock() mock_transposemd.reset_mock()
def test_MDE_workspaces_cannot_be_normalized(self): model = SliceViewerModel(self.ws_MDE_3D) self.assertFalse(model.can_normalize_workspace())
def test_title_for_matrixworkspace_just_contains_ws_name(self): model = SliceViewerModel(self.ws2d_histo) self.assertEqual('Sliceviewer - ws2d_histo', model.get_title())
def test_matrix_workspace_can_be_normalized_if_not_a_distribution(self): non_distrib_ws2d = _create_mock_matrixworkspace((1, 2, 3), (4, 5, 6), distribution=False, names=('a', 'b')) model = SliceViewerModel(non_distrib_ws2d) self.assertTrue(model.can_normalize_workspace())
def test_init_with_valid_MatrixWorkspace(self): mock_ws = MagicMock(spec=MatrixWorkspace) mock_ws.getNumberHistograms.return_value = 2 mock_ws.getDimension.return_value.getNBins.return_value = 2. self.assertIsNotNone(SliceViewerModel(mock_ws))
def test_title_for_mdhistoworkspace_with_original(self): with _attach_as_original(self.ws_MD_3D, self.ws_MDE_3D): model = SliceViewerModel(self.ws_MD_3D) self.assertEqual('Sliceviewer - ws_MD_3D', model.get_title())
def test_title_for_mdhistoworkspace_without_original_just_contains_ws_name( self): model = SliceViewerModel(self.ws_MD_3D) self.assertEqual('Sliceviewer - ws_MD_3D', model.get_title())