diff --git a/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_analysis.py b/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_analysis.py index de66f8a4..f439f60f 100644 --- a/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_analysis.py +++ b/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_analysis.py @@ -1,10 +1,10 @@ -from mantid.kernel import StringArrayProperty, Direction, StringListValidator, FloatArrayBoundedValidator, StringMandatoryValidator,\ - IntBoundedValidator, FloatArrayProperty +from mantid.kernel import StringArrayProperty, Direction, StringListValidator, IntArrayBoundedValidator, IntArrayProperty,\ + FloatArrayBoundedValidator, StringMandatoryValidator, IntBoundedValidator,FloatArrayProperty from mantid.api import FileProperty, FileAction, PythonAlgorithm,AlgorithmManager from mantid.simpleapi import CreateEmptyTableWorkspace, DeleteWorkspace, ReplaceSpecialValues, GroupWorkspaces, mtd,\ ConvertTableToMatrixWorkspace, ConjoinWorkspaces, Transpose, PlotPeakByLogValue,RenameWorkspace -from calibration_scripts.calibrate_vesuvio_helper_functions import EVSGlobals, EVSMiscFunctions - +from calibration_scripts.calibrate_vesuvio_helper_functions import EVSGlobals, EVSMiscFunctions,\ + InvalidDetectors import os import sys @@ -35,15 +35,21 @@ def PyInit(self): self.declareProperty('Mass', sys.float_info.max, doc="Mass of the sample in amu to be used when calculating energy. Default is Pb: 207.19") - greaterThanZero = FloatArrayBoundedValidator() - greaterThanZero.setLower(0) - self.declareProperty(FloatArrayProperty('DSpacings', [], greaterThanZero, Direction.Input), + greater_than_zero_float = FloatArrayBoundedValidator() + greater_than_zero_float.setLower(0) + self.declareProperty(FloatArrayProperty('DSpacings', [], greater_than_zero_float, Direction.Input), doc="List of d-spacings used to estimate the positions of peaks in TOF.") - self.declareProperty(FloatArrayProperty('E1FixedValueAndError', [], greaterThanZero, Direction.Input), + self.declareProperty(FloatArrayProperty('E1FixedValueAndError', [], greater_than_zero_float, Direction.Input), doc="Value at which to fix E1 and E1 error (form: E1 value, E1 Error). If no input is provided," "values will be calculated.") + detector_validator = IntArrayBoundedValidator() + detector_validator.setLower(EVSGlobals.DETECTOR_RANGE[0]) + detector_validator.setUpper(EVSGlobals.DETECTOR_RANGE[-1]) + self.declareProperty(IntArrayProperty('InvalidDetectors', [], detector_validator, Direction.Input), + doc="List of detectors to be marked as invalid (3-198), to be excluded from analysis calculations.") + self.declareProperty('Iterations', 2, validator=IntBoundedValidator(lower=1), doc="Number of iterations to perform. Default is 2.") @@ -111,7 +117,6 @@ def PyExec(self): # repeatedly fit L1, E1 and Theta parameters table_group = [] for i in range(self._iterations): - # calibrate theta for all detectors theta_fit = self._current_workspace + '_theta' self._run_calibration_fit(Samples=self._samples, Background=self._background, Function=self._theta_peak_function, @@ -123,10 +128,13 @@ def PyExec(self): # calibrate E1 for backscattering detectors and use the backscattering averaged value for all detectors E1_fit_back = self._current_workspace + '_E1_back' + invalid_detectors = \ self._run_calibration_fit(Samples=self._samples, Function='Voigt', Mode='SingleDifference', SpectrumRange=EVSGlobals.BACKSCATTERING_RANGE, InstrumentParameterWorkspace=self._param_table, Mass=self._sample_mass, OutputWorkspace=E1_fit_back, CreateOutput=self._create_output, - PeakType='Recoil', SharedParameterFitType=self._shared_parameter_fit_type) + PeakType='Recoil', SharedParameterFitType=self._shared_parameter_fit_type, + InvalidDetectors=self._invalid_detectors.get_all_invalid_detectors()) + self._invalid_detectors.add_invalid_detectors(invalid_detectors) E1_peak_fits_back = mtd[self._current_workspace + '_E1_back_Peak_Parameters'].getNames() self._calculate_final_energy(E1_peak_fits_back, EVSGlobals.BACKSCATTERING_RANGE, self._shared_parameter_fit_type != "Individual") @@ -136,10 +144,13 @@ def PyExec(self): # calibrate L1 for frontscattering detectors based on the averaged E1 value and calibrated theta values E1_fit_front = self._current_workspace + '_E1_front' + invalid_detectors += \ self._run_calibration_fit(Samples=self._samples, Function='Voigt', Mode='SingleDifference', SpectrumRange=EVSGlobals.FRONTSCATTERING_RANGE, InstrumentParameterWorkspace=self._param_table, Mass=self._sample_mass, OutputWorkspace=E1_fit_front, CreateOutput=self._create_output, - PeakType='Recoil', SharedParameterFitType=self._shared_parameter_fit_type) + PeakType='Recoil', SharedParameterFitType=self._shared_parameter_fit_type, + InvalidDetectors=self._invalid_detectors.get_all_invalid_detectors()) + self._invalid_detectors.add_invalid_detectors(invalid_detectors) E1_peak_fits_front = mtd[self._current_workspace + '_E1_front_Peak_Parameters'].getNames() self._calculate_final_flight_path(E1_peak_fits_front[0], EVSGlobals.FRONTSCATTERING_RANGE) @@ -178,14 +189,18 @@ def _run_calibration_fit(self, *args, **kwargs): @param args - positional arguments to the algorithm @param kwargs - key word arguments to the algorithm + @returns - returns list of invalid detectors so set in overarching alg """ from mantid.simpleapi import set_properties alg = AlgorithmManager.create('EVSCalibrationFit') alg.initialize() alg.setRethrows(True) + set_properties(alg, *args, **kwargs) alg.execute() + return alg.getProperty("InvalidDetectorsOut").value.tolist() + def _calculate_time_delay(self, table_name, spec_list): """ Calculate time delay from frontscattering detectors. @@ -245,13 +260,7 @@ def _calculate_final_flight_path(self, peak_table, spec_list): theta = EVSMiscFunctions.read_table_column(self._current_workspace, 'theta', spec_list) r_theta = EVSMiscFunctions.calculate_r_theta(self._sample_mass, theta) - peak_centres = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzPos', spec_list) - peak_centres_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzPos_Err', spec_list) - invalid_spectra = EVSMiscFunctions.identify_invalid_spectra(peak_table, peak_centres, peak_centres_errors, spec_list) - peak_centres[invalid_spectra] = np.nan - - print(f'Invalid Spectra Index Found and Marked NAN: {invalid_spectra.flatten()} from Spectra Index List:' - f'{[ x -3 for x in spec_list]}') + peak_centres = self._invalid_detectors.filter_peak_centres_for_invalid_detectors(spec_list, peak_table) delta_t = (peak_centres - t0) / 1e+6 delta_t_error = t0_error / 1e+6 @@ -328,11 +337,7 @@ def _calculate_final_energy(self, peak_table, spec_list, calculate_global): theta = EVSMiscFunctions.read_table_column(self._current_workspace, 'theta', spec_list) r_theta = EVSMiscFunctions.calculate_r_theta(self._sample_mass, theta) - peak_centres = EVSMiscFunctions.read_fitting_result_table_column(peak_table[0], 'f1.LorentzPos', spec_list) - peak_centres_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table[0], 'f1.LorentzPos_Err', spec_list) - - invalid_spectra = EVSMiscFunctions.identify_invalid_spectra(peak_table[0], peak_centres, peak_centres_errors, spec_list) - peak_centres[invalid_spectra] = np.nan + peak_centres = self._invalid_detectors.filter_peak_centres_for_invalid_detectors(spec_list, peak_table[0]) delta_t = (peak_centres - t0) / 1e+6 v1 = (L0 * r_theta + L1) / delta_t @@ -381,6 +386,7 @@ def _setup(self): self._sample_mass = self.getProperty("Mass").value self._d_spacings = self.getProperty("DSpacings").value.tolist() self._E1_value_and_error = self.getProperty("E1FixedValueAndError").value.tolist() + self._invalid_detectors = InvalidDetectors(self.getProperty("InvalidDetectors").value.tolist()) self._shared_parameter_fit_type = self.getProperty("SharedParameterFitType").value self._calc_L0 = self.getProperty("CalculateL0").value self._make_IP_file = self.getProperty("CreateIPFile").value diff --git a/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_fit.py b/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_fit.py index 1fbe846b..efef766a 100644 --- a/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_fit.py +++ b/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_fit.py @@ -6,7 +6,7 @@ ReplaceSpecialValues, FindPeaks, GroupWorkspaces, mtd, Plus, LoadVesuvio, LoadRaw, ConvertToDistribution, FindPeakBackground,\ ExtractSingleSpectrum, SumSpectra, AppendSpectra, CloneWorkspace, Fit, MaskDetectors, ExtractUnmaskedSpectra, CreateWorkspace from functools import partial -from calibration_scripts.calibrate_vesuvio_helper_functions import EVSGlobals, EVSMiscFunctions +from calibration_scripts.calibrate_vesuvio_helper_functions import EVSGlobals, EVSMiscFunctions, InvalidDetectors import os import sys @@ -68,6 +68,14 @@ def PyInit(self): doc='Calculate shared parameters using an individual and/or' 'global fit.', validator=shared_fit_type_validator) + detector_validator = IntArrayBoundedValidator() + detector_validator.setLower(EVSGlobals.DETECTOR_RANGE[0]) + detector_validator.setUpper(EVSGlobals.DETECTOR_RANGE[-1]) + self.declareProperty(IntArrayProperty('InvalidDetectors', [], detector_validator, Direction.Input), + doc="List of detectors to be marked as invalid (3-198), to be excluded from analysis calculations.") + self.declareProperty(IntArrayProperty('InvalidDetectorsOut', [], detector_validator, Direction.Output), + doc="List of detectors found as invalid to be output.") + self.declareProperty( ITableWorkspaceProperty("InstrumentParameterWorkspace", "", Direction.Input, PropertyMode.Optional), doc='Workspace contain instrument parameters.') @@ -91,6 +99,9 @@ def PyExec(self): if self._create_output and not self._fitting_bragg_peaks: self._generate_fit_workspaces() + # set invalid detectors output param + self.setProperty("InvalidDetectorsOut", self._invalid_detectors.get_all_invalid_detectors()) + # clean up workspaces if self._param_file != "": DeleteWorkspace(self._param_table) @@ -112,6 +123,7 @@ def _setup_class_variables_from_properties(self): self._sample_mass = self.getProperty("Mass").value self._create_output = self.getProperty("CreateOutput").value self._shared_parameter_fit_type = self.getProperty("SharedParameterFitType").value + self._invalid_detectors = InvalidDetectors(self.getProperty("InvalidDetectors").value.tolist()) def _setup_spectra_list(self): self._spec_list = self.getProperty("SpectrumRange").value.tolist() @@ -549,6 +561,7 @@ def _fit_peaks(self): self._output_parameter_tables = [] self._peak_fit_workspaces = [] + for peak_index, estimated_peak_positions in enumerate(estimated_peak_positions_all_peaks): self._peak_fit_workspaces_by_spec = [] @@ -585,12 +598,10 @@ def _shared_parameter_fit(self, output_parameter_table_name, output_parameter_ta initial_params = {'A0': 0.0, 'A1': 0.0, 'LorentzAmp': init_Lorentz_Amp, 'LorentzPos': init_Lorentz_Pos, 'LorentzFWHM': init_Lorentz_FWHM, 'GaussianFWHM': init_Gaussian_FWHM} - peak_centres = EVSMiscFunctions.read_fitting_result_table_column(output_parameter_table_name, 'f1.LorentzPos', self._spec_list) - peak_centres_errors = EVSMiscFunctions.read_fitting_result_table_column(output_parameter_table_name, 'f1.LorentzPos_Err', - self._spec_list) - invalid_spectra = EVSMiscFunctions.identify_invalid_spectra(output_parameter_table_name, peak_centres, peak_centres_errors, - self._spec_list) - self._fit_shared_parameter(invalid_spectra, initial_params, output_parameter_table_headers) + self._invalid_detectors.identify_and_set_invalid_detectors_from_range(self._spec_list, output_parameter_table_name) + + self._fit_shared_parameter(self._invalid_detectors.get_invalid_detectors_index(self._spec_list), initial_params, + output_parameter_table_headers) def _fit_peak(self, peak_index, spec_index, peak_position, output_parameter_table_name, output_parameter_table_headers): diff --git a/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_helper_functions.py b/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_helper_functions.py index 964fc42c..9964e22a 100644 --- a/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_helper_functions.py +++ b/unpackaged/vesuvio_calibration/calibration_scripts/calibrate_vesuvio_helper_functions.py @@ -92,35 +92,6 @@ def calculate_r_theta(sample_mass, thetas): return r_theta - @staticmethod - def identify_invalid_spectra(peak_table, peak_centres, peak_centres_errors, spec_list): - """ - Inspect fitting results, and identify the fits associated with invalid spectra. These are spectra associated with detectors - which have lost foil coverage following a recent reduction in distance from source to detectors. - - @param peak_table - name of table containing fitted parameters each spectra. - @param spec_list - spectrum range to inspect. - @return a list of invalid spectra. - """ - peak_Gaussian_FWHM = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.GaussianFWHM', spec_list) - peak_Gaussian_FWHM_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.GaussianFWHM_Err', spec_list) - peak_Lorentz_FWHM = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzFWHM', spec_list) - peak_Lorentz_FWHM_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzFWHM_Err', spec_list) - peak_Lorentz_Amp = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzAmp', spec_list) - peak_Lorentz_Amp_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzAmp_Err', spec_list) - - invalid_spectra = np.argwhere((np.isinf(peak_Lorentz_Amp_errors)) | (np.isnan(peak_Lorentz_Amp_errors)) | \ - (np.isinf(peak_centres_errors)) | (np.isnan(peak_centres_errors)) | \ - (np.isnan(peak_Gaussian_FWHM_errors)) | (np.isinf(peak_Gaussian_FWHM_errors)) | \ - (np.isnan(peak_Lorentz_FWHM_errors)) | (np.isinf(peak_Lorentz_FWHM_errors)) | \ - (np.isnan(peak_Lorentz_Amp_errors)) | (np.isinf(peak_Lorentz_Amp_errors)) | \ - (np.absolute(peak_Gaussian_FWHM_errors) > np.absolute(peak_Gaussian_FWHM)) | \ - (np.absolute(peak_Lorentz_FWHM_errors) > np.absolute(peak_Lorentz_FWHM)) | \ - (np.absolute(peak_Lorentz_Amp_errors) > np.absolute(peak_Lorentz_Amp)) | \ - (np.absolute(peak_centres_errors) > np.absolute(peak_centres))) - - return invalid_spectra - # The IP text file load function skips the first 3 rows of the text file. # This assumes that there is one line for the file header and 2 lines for # parameters of monitor 1 and monitor 2, respectively. @@ -216,3 +187,122 @@ def generate_fit_function_header(function_type, error=False): raise ValueError("Unsupported fit function type: %s" % function_type) return {k: v + error_str for k, v in func_header.items()} + + +class InvalidDetectors: + + def __init__(self, invalid_detector_list): + if invalid_detector_list: + self._invalid_detectors_front = self._preset_invalid_detectors(invalid_detector_list, EVSGlobals.FRONTSCATTERING_RANGE) + self._invalid_detectors_back = self._preset_invalid_detectors(invalid_detector_list, EVSGlobals.BACKSCATTERING_RANGE) + self._detectors_preset = True + else: + self._invalid_detectors_front = np.array([]) + self._invalid_detectors_back = np.array([]) + self._detectors_preset = False + + def add_invalid_detectors(self, invalid_detector_list): + """ + Takes a list of invalid spectra, adds unique entries to the stored np arrays. + @param invalid_detector_list - list of detectors to append to detector list, if unique. + """ + self._invalid_detectors_front = np.array([[x] for x in sorted(set(self._invalid_detectors_front.ravel()).union( + set(self._preset_invalid_detectors(invalid_detector_list, EVSGlobals.FRONTSCATTERING_RANGE).ravel())))]) + self._invalid_detectors_back = np.array([[x] for x in sorted(set(self._invalid_detectors_back.ravel()).union( + set(self._preset_invalid_detectors(invalid_detector_list, EVSGlobals.BACKSCATTERING_RANGE).ravel())))]) + + def get_all_invalid_detectors(self): + return [i + EVSGlobals.BACKSCATTERING_RANGE[0] for i in self._invalid_detectors_back.flatten().tolist()] + \ + [j + EVSGlobals.FRONTSCATTERING_RANGE[0] for j in self._invalid_detectors_front.flatten().tolist()] + + def get_invalid_detectors_index(self, desired_range): + if desired_range == EVSGlobals.FRONTSCATTERING_RANGE: + return self._invalid_detectors_front.flatten().tolist() + elif desired_range == EVSGlobals.BACKSCATTERING_RANGE: + return self._invalid_detectors_back.flatten().tolist() + else: + raise AttributeError("desired range invalid - must represent either front or back detectors.") + + @staticmethod + def _preset_invalid_detectors(invalid_detector_list_full_range, desired_range): + return np.array([[x - desired_range[0]] for x in invalid_detector_list_full_range if desired_range[0] <= x <= desired_range[-1]]) + + def filter_peak_centres_for_invalid_detectors(self, detector_range, peak_table): + """ + Finds invalid detectors and filters the peak centres. Caches the invalid detectors found to avoid repeat identification. + @param detector_range detectors to consider, must be either FRONT or BACKSCATTERING range. + @param peak_table - name of table containing fitted parameters each spectra. + + @returns peak_centres - a list of peak fitted peak centres, with those that represent invalid detectors marked nan. + """ + + invalid_detectors = self.identify_and_set_invalid_detectors_from_range(detector_range, peak_table) + peak_centres = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzPos', detector_range) + peak_centres[invalid_detectors] = np.nan + return peak_centres + + def identify_and_set_invalid_detectors_from_range(self, detector_range, peak_table): + """ + Finds invalid detectors and caches the invalid detectors. Will not look to calculate if invalid detectors already exist. + @param detector_range detectors to consider, must be either FRONT or BACKSCATTERING range. + @param peak_table - name of table containing fitted parameters each spectra. + + @returns invalid_detectors - a list of the index's of invalid detector, in the context of the range they belong to. + """ + + peak_centres = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzPos', detector_range) + peak_centres_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzPos_Err', detector_range) + + if detector_range == EVSGlobals.FRONTSCATTERING_RANGE: + if not self._detectors_preset and not self._invalid_detectors_front.any(): + self._invalid_detectors_front = self._identify_invalid_spectra(peak_table, peak_centres, peak_centres_errors, + detector_range) + self._print_invalid_detectors(front_detector_range=True) + return self._invalid_detectors_front + elif detector_range == EVSGlobals.BACKSCATTERING_RANGE: + if not self._detectors_preset and not self._invalid_detectors_back.any(): + self._invalid_detectors_back = self._identify_invalid_spectra(peak_table, peak_centres, peak_centres_errors, + detector_range) + self._print_invalid_detectors(front_detector_range=False) + return self._invalid_detectors_back + else: + raise AttributeError("Spec list invalid - must represent either front or back detectors.") + + def _print_invalid_detectors(self, front_detector_range): + if front_detector_range: + invalid_detectors = self._invalid_detectors_front + detector_range = EVSGlobals.FRONTSCATTERING_RANGE + else: + invalid_detectors = self._invalid_detectors_back + detector_range = EVSGlobals.BACKSCATTERING_RANGE + + print(f'Invalid Spectra Index Found and Marked NAN: {invalid_detectors} from Spectra Index List:' + f'{[x - EVSGlobals.DETECTOR_RANGE[0] for x in detector_range]}') + + @staticmethod + def _identify_invalid_spectra(peak_table, peak_centres, peak_centres_errors, spec_list): + """ + Inspect fitting results, and identify the fits associated with invalid spectra. These are spectra associated with detectors + which have lost foil coverage following a recent reduction in distance from source to detectors. + + @param peak_table - name of table containing fitted parameters each spectra. + @param spec_list - spectrum range to inspect. + @return a list of invalid spectra. + """ + peak_Gaussian_FWHM = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.GaussianFWHM', spec_list) + peak_Gaussian_FWHM_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.GaussianFWHM_Err', spec_list) + peak_Lorentz_FWHM = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzFWHM', spec_list) + peak_Lorentz_FWHM_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzFWHM_Err', spec_list) + peak_Lorentz_Amp = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzAmp', spec_list) + peak_Lorentz_Amp_errors = EVSMiscFunctions.read_fitting_result_table_column(peak_table, 'f1.LorentzAmp_Err', spec_list) + + invalid_spectra = np.argwhere((np.isinf(peak_Lorentz_Amp_errors)) | (np.isnan(peak_Lorentz_Amp_errors)) | \ + (np.isinf(peak_centres_errors)) | (np.isnan(peak_centres_errors)) | \ + (np.isnan(peak_Gaussian_FWHM_errors)) | (np.isinf(peak_Gaussian_FWHM_errors)) | \ + (np.isnan(peak_Lorentz_FWHM_errors)) | (np.isinf(peak_Lorentz_FWHM_errors)) | \ + (np.isnan(peak_Lorentz_Amp_errors)) | (np.isinf(peak_Lorentz_Amp_errors)) | \ + (np.absolute(peak_Gaussian_FWHM_errors) > np.absolute(peak_Gaussian_FWHM)) | \ + (np.absolute(peak_Lorentz_FWHM_errors) > np.absolute(peak_Lorentz_FWHM)) | \ + (np.absolute(peak_Lorentz_Amp_errors) > np.absolute(peak_Lorentz_Amp)) | \ + (np.absolute(peak_centres_errors) > np.absolute(peak_centres))) + return invalid_spectra \ No newline at end of file diff --git a/unpackaged/vesuvio_calibration/tests/system/test_system_analysis.py b/unpackaged/vesuvio_calibration/tests/system/test_system_analysis.py index 779568cd..45e8aed0 100644 --- a/unpackaged/vesuvio_calibration/tests/system/test_system_analysis.py +++ b/unpackaged/vesuvio_calibration/tests/system/test_system_analysis.py @@ -10,7 +10,7 @@ from tests.testhelpers.system_test_misc_functions import assert_allclose_excluding_bad_detectors from calibration_scripts.calibrate_vesuvio_analysis import EVSCalibrationAnalysis from calibration_scripts.calibrate_vesuvio_fit import EVSCalibrationFit -from copy import copy +from copy import copy, deepcopy from os import path @@ -175,6 +175,27 @@ def test_copper_create_output(self, load_file_mock): 165, 167, 168, 169, 170, 182, 191, 192]}) self._assert_parameters_match_expected(params_table, detector_specific_r_tols) + @patch('calibration_scripts.calibrate_vesuvio_fit.EVSCalibrationFit._load_file') + def test_copper_create_invalid_detectors_specified(self, load_file_mock): + self._setup_copper_test() + self._output_workspace = "copper_analysis_test" + + load_file_mock.side_effect = self._load_file_side_effect + + self._create_evs_calibration_alg() + self._alg.setProperty("InvalidDetectors", [3, 5, 7, 141, 144, 149, 150, 159, 161, 163, 166, 167, 168, 170, 171, 172, 173, 185, 194, + 195]) + params_table = self._execute_evs_calibration_analysis() + + # Specify detectors tolerances set by user, then update with those to mask as invalid. + detector_specific_r_tols = {"L1": {116: 0.65, 170: 0.75}} + detector_specific_r_tols["L1"].update({k: TestConstants.INVALID_DETECTOR for k in [138, 141, 146, 147, 156, 158, 160, 163, + 164, 165, 167, 168, 169, 170, 182, 191, 192]}) + self.assertRaises(AssertionError, self._assert_parameters_match_expected, *[params_table, detector_specific_r_tols]) + + detector_specific_r_tols["L1"].update({k: TestConstants.INVALID_DETECTOR for k in [0, 2, 4]}) + self._assert_parameters_match_expected(params_table, detector_specific_r_tols) + @patch('calibration_scripts.calibrate_vesuvio_fit.EVSCalibrationFit._load_file') def test_copper_with_individual_and_global_fit(self, load_file_mock): self._setup_copper_test() @@ -226,7 +247,7 @@ def _assert_E1_parameters_match_expected(self, params_table, rel_tolerance, fit_ self.assertAlmostEqual(global_E1, EVSGlobals.ENERGY_ESTIMATE, delta=EVSGlobals.ENERGY_ESTIMATE*rel_tolerance) def _assert_parameters_match_expected(self, params_table, tolerances=None): - rel_tol_theta, rel_tol_L1 = self._extract_tolerances(tolerances) + rel_tol_theta, rel_tol_L1 = self._extract_tolerances(deepcopy(tolerances)) theta_errors = self._assert_theta_parameters_match_expected(params_table, rel_tol_theta) L1_errors = self._assert_L1_parameters_match_expected(params_table, rel_tol_L1) diff --git a/unpackaged/vesuvio_calibration/tests/unit/test_calibrate_vesuvio_misc.py b/unpackaged/vesuvio_calibration/tests/unit/test_calibrate_vesuvio_misc.py index d995dff3..fe45943c 100644 --- a/unpackaged/vesuvio_calibration/tests/unit/test_calibrate_vesuvio_misc.py +++ b/unpackaged/vesuvio_calibration/tests/unit/test_calibrate_vesuvio_misc.py @@ -1,4 +1,4 @@ -from calibration_scripts.calibrate_vesuvio_helper_functions import EVSMiscFunctions +from calibration_scripts.calibrate_vesuvio_helper_functions import EVSMiscFunctions, InvalidDetectors from mock import MagicMock, patch import unittest @@ -44,7 +44,7 @@ def test_identify_invalid_spectra_no_invalid(self, mock_mtd): mock_mtd.__getitem__.return_value = ws_mock np.testing.assert_equal(np.argwhere([]), - EVSMiscFunctions.identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) + InvalidDetectors._identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) @patch('calibration_scripts.calibrate_vesuvio_helper_functions.mtd') def test_identify_invalid_spectra_nan_in_errors(self, mock_mtd): @@ -56,7 +56,7 @@ def test_identify_invalid_spectra_nan_in_errors(self, mock_mtd): mock_mtd.__getitem__.return_value = ws_mock np.testing.assert_equal(np.argwhere(np.array([True, False, True])), - EVSMiscFunctions.identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) + InvalidDetectors._identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) @patch('calibration_scripts.calibrate_vesuvio_helper_functions.mtd') def test_identify_invalid_spectra_inf_in_errors(self, mock_mtd): @@ -68,7 +68,7 @@ def test_identify_invalid_spectra_inf_in_errors(self, mock_mtd): mock_mtd.__getitem__.return_value = ws_mock np.testing.assert_equal(np.argwhere(np.array([True, True, False])), - EVSMiscFunctions.identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) + InvalidDetectors._identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) @patch('calibration_scripts.calibrate_vesuvio_helper_functions.mtd') def test_identify_invalid_spectra_error_greater_than_peak(self, mock_mtd): @@ -80,7 +80,124 @@ def test_identify_invalid_spectra_error_greater_than_peak(self, mock_mtd): mock_mtd.__getitem__.return_value = ws_mock np.testing.assert_equal(np.argwhere(np.array([False, True, True])), - EVSMiscFunctions.identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) + InvalidDetectors._identify_invalid_spectra('peak_table', [5, 10, 20], [0.1, 0.15, 0.2], [0, 2])) + + def test_create_empty_invalid_detectors(self): + invalid_detectors = InvalidDetectors([]) + self.assertEqual(invalid_detectors._invalid_detectors_back.tolist(), []) + self.assertEqual(invalid_detectors._invalid_detectors_front.tolist(), []) + self.assertEqual(invalid_detectors._detectors_preset, False) + + def test_create_invalid_detectors_back(self): + invalid_detectors = InvalidDetectors([10, 20, 30]) + self.assertEqual(invalid_detectors._invalid_detectors_back.tolist(), [[7], [17], [27]]) + self.assertEqual(invalid_detectors._invalid_detectors_front.tolist(), []) + self.assertEqual(invalid_detectors._detectors_preset, True) + + def test_create_invalid_detectors_front(self): + invalid_detectors = InvalidDetectors([150, 160, 170]) + self.assertEqual(invalid_detectors._invalid_detectors_back.tolist(), []) + self.assertEqual(invalid_detectors._invalid_detectors_front.tolist(), [[15], [25], [35]]) + self.assertEqual(invalid_detectors._detectors_preset, True) + + def test_create_invalid_detectors(self): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + self.assertEqual(invalid_detectors._invalid_detectors_back.tolist(), [[7], [17], [27]]) + self.assertEqual(invalid_detectors._invalid_detectors_front.tolist(), [[15], [25], [35]]) + + def test_get_all_detectors(self): + input_invalid_detectors = [10, 20, 30, 150, 160, 170] + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + self.assertEqual(invalid_detectors.get_all_invalid_detectors(), input_invalid_detectors) + + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.EVSMiscFunctions.read_fitting_result_table_column') + def test_filter_peak_centres_for_invalid_detectors_front(self, mock_read_fitting_result): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + peak_table = 'input_peak_table' + mock_read_fitting_result.return_value = np.array([[float(x)] for x in range(3, 198, 1)]) + + out_peak_centres = invalid_detectors.filter_peak_centres_for_invalid_detectors([3, 134], peak_table) + self.assertEqual(list(np.argwhere(np.isnan(out_peak_centres)).transpose()[0]), [7, 17, 27]) + + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.EVSMiscFunctions.read_fitting_result_table_column') + def test_filter_peak_centres_for_invalid_detectors_back(self, mock_read_fitting_result): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + peak_table = 'input_peak_table' + mock_read_fitting_result.return_value = np.array([[float(x)] for x in range(3, 198, 1)]) + + out_peak_centres = invalid_detectors.filter_peak_centres_for_invalid_detectors([135, 198], peak_table) + self.assertEqual(list(np.argwhere(np.isnan(out_peak_centres)).transpose()[0]), [15, 25, 35]) + + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.EVSMiscFunctions.read_fitting_result_table_column') + def test_filter_peak_centres_for_invalid_detectors_invalid_range(self, mock_read_fitting_result): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + peak_table = 'input_peak_table' + with self.assertRaises(AttributeError): + invalid_detectors.filter_peak_centres_for_invalid_detectors([10, 20], peak_table) + + def test_get_invalid_detectors_index_back(self): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + invalid_detectors_index = invalid_detectors.get_invalid_detectors_index([3, 134]) + self.assertEqual(invalid_detectors_index, [7, 17, 27]) + + def test_get_invalid_detectors_index_front(self): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + invalid_detectors_index = invalid_detectors.get_invalid_detectors_index([135, 198]) + self.assertEqual(invalid_detectors_index, [15, 25, 35]) + + def test_get_invalid_detectors_index_invalid_range(self): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + with self.assertRaises(AttributeError): + invalid_detectors.get_invalid_detectors_index([10, 20]) + + def test_add_invalid_detectors(self): + invalid_detectors = InvalidDetectors([10, 20, 30, 150, 160, 170]) + invalid_detectors.add_invalid_detectors([10, 20, 25, 30, 180, 190]) + self.assertEqual(invalid_detectors.get_all_invalid_detectors(), [10, 20, 25, 30, 150, 160, 170, 180, 190]) + + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.InvalidDetectors._identify_invalid_spectra') + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.EVSMiscFunctions.read_fitting_result_table_column') + def test_add_invalid_detectors_no_preset_identify_called_front(self, mock_read_fitting_result, mock_identify): + invalid_detectors = InvalidDetectors([]) + invalid_detectors.add_invalid_detectors([180, 190]) + self.assertEqual(invalid_detectors.get_all_invalid_detectors(), [180, 190]) + self.assertEqual(invalid_detectors._detectors_preset, False) + + peak_table = 'input_peak_table' + invalid_detectors.filter_peak_centres_for_invalid_detectors([3, 134], peak_table) + mock_identify.assert_called_once() + + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.InvalidDetectors._identify_invalid_spectra') + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.EVSMiscFunctions.read_fitting_result_table_column') + def test_add_invalid_detectors_no_preset_identify_called_back(self, mock_read_fitting_result, mock_identify): + invalid_detectors = InvalidDetectors([]) + invalid_detectors.add_invalid_detectors([31, 32]) + self.assertEqual(invalid_detectors.get_all_invalid_detectors(), [31, 32]) + self.assertEqual(invalid_detectors._detectors_preset, False) + + peak_table = 'input_peak_table' + invalid_detectors.filter_peak_centres_for_invalid_detectors([135, 198], peak_table) + mock_identify.assert_called_once() + + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.InvalidDetectors._identify_invalid_spectra') + @patch('calibration_scripts.calibrate_vesuvio_helper_functions' + '.EVSMiscFunctions.read_fitting_result_table_column') + def test_add_invalid_detectors_preset_identify_not_called(self, mock_read_fitting_result, mock_identify): + invalid_detectors = InvalidDetectors([180, 190]) + self.assertEqual(invalid_detectors.get_all_invalid_detectors(), [180, 190]) + self.assertEqual(invalid_detectors._detectors_preset, True) + + peak_table = 'input_peak_table' + invalid_detectors.filter_peak_centres_for_invalid_detectors([3, 134], peak_table) + mock_identify.assert_not_called() if __name__ == '__main__':