Пример #1
0
    def test_fit_bank(self):
        with self.assertRaises(AssertionError) as exception_info:
            fit_bank('I_am_not_here', 'bank42')
        assert 'Input workspace I_am_not_here does not exists' in str(exception_info.exception)

        control = self.cases['123455_bank20']  # a bank with a reasonable wire scan
        with self.assertRaises(AssertionError) as exception_info:
            fit_bank(control, 'bank20', shadow_height=-1000)
        assert 'shadow height must be positive' in str(exception_info.exception)

        with self.assertRaises(AssertionError) as exception_info:
            fit_bank(control, 'tree/bank51')
        assert 'The bank name must be of the form' in str(exception_info.exception)

        with self.assertRaises(AssertionError) as exception_info:
            fit_bank(self.cases['123555_bank20'], 'bank20')
        assert 'Insufficient counts per pixel in workspace CORELLI_123555_bank20' in str(exception_info.exception)

        fit_bank(control, 'bank20')
        # Expect default name for calibration table
        assert AnalysisDataService.doesExist('CalibTable')
        # Expect default name for peak pixel position table
        assert AnalysisDataService.doesExist('PeakTable')
        DeleteWorkspaces(['CalibTable', 'PeakTable'])

        fit_bank(control, 'bank20', calibration_table='table_20', peak_pixel_positions_table='pixel_20')
        assert AnalysisDataService.doesExist('table_20')
        assert AnalysisDataService.doesExist('pixel_20')

        DeleteWorkspaces(['CalibTable', 'PeakTable'])  # a bit of clean-up
Пример #2
0
    def test_calibrate_bank(self):

        # control bank, it has no problems. Thus, no mask to be created
        calibration, mask = calibrate_bank(self.cases['123455_bank20'], 'bank20', 'calibration_table')
        assert calibration.rowCount() == 256 * 16
        assert calibration.columnCount() == 3
        assert AnalysisDataService.doesExist('calibration_table')
        assert mask is None
        assert AnalysisDataService.doesExist('MaskTable') is False

        # tubes 3, 8, and 13 have very faint wire shadows. Thus, mask these tubes
        calibration, mask = calibrate_bank(self.cases['124023_bank14'], 'bank14',
                                           calibration_table='calibration_table')
        assert calibration.rowCount() == 256 * (16 - 3)
        assert calibration.columnCount() == 2  # Detector ID, Position
        assert AnalysisDataService.doesExist('calibration_table')
        assert mask.rowCount() == 256 * 3
        assert mask.columnCount() == 1
        assert AnalysisDataService.doesExist('MaskTable')

        # check for the summary workspace
        calibrate_bank(self.cases['123455_bank20'], 'bank20', 'calibration_table',
                       criterium_kwargs=dict(summary='summary'))
        assert AnalysisDataService.doesExist('summary')
        workspace = mtd['summary']
        axis = workspace.getAxis(1)
        assert [axis.label(workspace_index) for workspace_index in (0, 1, 2)] == ['success', 'deviation', 'Z-score']
        self.assertEqual(min(workspace.readY(0)), 1.0)
        self.assertAlmostEqual(max(workspace.readY(2)), 1.728, delta=0.001)

        DeleteWorkspaces(['calibration_table', 'MaskTable', 'summary'])  # a bit of clean-up
Пример #3
0
    def test_calibrate_bank(self):

        # control bank, it has no problems. Thus, no mask to be created
        calibration, mask = calibrate_bank(self.cases['123455_bank20'], 'bank20', 'calibration_table')
        assert calibration.rowCount() == 256 * 16
        assert calibration.columnCount() == 2
        assert AnalysisDataService.doesExist('calibration_table')
        assert mask is None
        assert AnalysisDataService.doesExist('MaskTable') is False
        DeleteWorkspaces(['calibration_table'])  # clean-up

        # beam center intensity spills over adjacent tubes, tube15 and tube16
        calibration, mask = calibrate_bank(self.cases['123454_bank58'], 'bank58', 'calibration_table')
        assert calibration.rowCount() == 256 * (16 - 2)
        assert calibration.columnCount() == 2  # Detector ID, Position
        assert AnalysisDataService.doesExist('calibration_table')
        assert mask.rowCount() == 256 * 2
        assert mask.columnCount() == 1
        assert AnalysisDataService.doesExist('MaskTable')
        DeleteWorkspaces(['calibration_table', 'MaskTable'])  # clean-up

        # check for the fits workspace
        calibrate_bank(self.cases['123455_bank20'], 'bank20', 'calibration_table', fit_results='fits')
        assert AnalysisDataService.doesExist('fits')
        workspace = mtd['fits']
        axis = workspace.getAxis(1)
        labels = [axis.label(i) for i in range(workspace.getNumberHistograms())]
        assert labels == ['success', 'deviation', 'Z-score', 'A0', 'A1', 'A2']
        assert_allclose(workspace.readY(0), [1.0] * TUBES_IN_BANK)  # success status for first tube
        self.assertAlmostEqual(max(workspace.readY(2)), 2.73, delta=0.01)  # maximum Z-score
        self.assertAlmostEqual(max(workspace.readY(3)), -0.445, delta=0.001)  # maximum A0 value
        self.assertAlmostEqual(max(workspace.readE(3)), 1.251, delta=0.001)  # maximum A0 error
        DeleteWorkspaces(['calibration_table', 'fits'])  # a bit of clean-up
Пример #4
0
 def tearDown(self) -> None:
     to_delete = [
         w for w in [self.workspace, self.table]
         if AnalysisDataService.doesExist(w)
     ]
     if len(to_delete) > 0:
         DeleteWorkspaces(to_delete)
Пример #5
0
def mask_bank(bank_name: str, tubes_fit_success: np.ndarray, output_table: str) -> Optional[TableWorkspace]:
    r"""
    Creates a single-column `TableWorkspace` object containing the detector ID's of the
    unsuccessfully fitted tubes

    If all tubes were fit successfully, no `TableWorkspace` is created, and `None` is returned.

    :param bank_name: a string of the form 'bankI' where 'I' is a bank number
    :param tubes_fit_success: array of boolean containing a True/False entry for each tube, indicating wether
    the tube was successfully calibrated.
    :param output_table: name of the output TableWorkspace containing one column for detector ID from tubes
    not successfully calibrated.

    :raise AssertionError: the string bank_name does not follow the pattern 'bankI' where 'I' in an integer
    :return: name of the mask TableWorkspace. Returns `None` if no TableWorkspace is created.
    """
    assert re.match(r'^bank\d+$', bank_name), 'The bank name must be of the form "bankI" where "I" in an integer'
    if False not in tubes_fit_success:
        return None  # al tubes were fit successfully
    bank_number = bank_name[4:]  # drop 'bank' from bank_name
    tube_numbers = 1 + np.where(tubes_fit_success == False)[0]  # noqa E712 unsuccessfully fitted tube numbers
    tube_numbers = ','.join([str(n) for n in tube_numbers])  # failing tubes as a string
    detector_ids = MaskBTP(Instrument='CORELLI', Bank=bank_number, Tube=tube_numbers)
    table = CreateEmptyTableWorkspace(OutputWorkspace=output_table)
    table.addColumn('long64', 'Detector ID')
    [table.addRow([detector_id]) for detector_id in detector_ids.tolist()]
    if AnalysisDataService.doesExist('CORELLIMaskBTP'):
        DeleteWorkspaces(['CORELLIMaskBTP'])
    return mtd[output_table]
Пример #6
0
    def test_fit_bank(self):
        control = self.cases['123455_bank20']  # a bank with a reasonable wire scan

        with self.assertRaises(AssertionError) as exception_info:
            fit_bank('I_am_not_here', 'bank42')
        with self.assertRaises(AssertionError) as exception_info:
            fit_bank(control, 'bank20', shadow_height=-1000)
        assert 'shadow height must be positive' in str(exception_info.exception)

        with self.assertRaises(AssertionError) as exception_info:
            fit_bank(control, 'tree/bank51')
        assert 'The bank name must be of the form' in str(exception_info.exception)

        with self.assertRaises(AssertionError) as exception_info:
            fit_bank(self.cases['123555_bank20'], 'bank20')
        assert 'Insufficient counts per pixel in workspace CORELLI_123555_bank20' in str(exception_info.exception)

        fit_bank(control, 'bank20', parameters_table_group='parameters_table')
        # Expect default name for calibration table
        assert AnalysisDataService.doesExist('CalibTable')
        # Expect default name for peak pixel position table
        assert AnalysisDataService.doesExist('PeakTable')
        # Expect default name for peak pixel position table
        assert AnalysisDataService.doesExist('PeakYTable')
        # assert the parameters tables are created
        assert AnalysisDataService.doesExist('parameters_table')
        for tube_number in range(TUBES_IN_BANK):
            assert AnalysisDataService.doesExist(f'parameters_table_{tube_number}')
        DeleteWorkspaces(['CalibTable', 'PeakTable', 'PeakYTable', 'parameters_table'])

        fit_bank(control, 'bank20', calibration_table='table_20', peak_pixel_positions_table='pixel_20')
        assert AnalysisDataService.doesExist('table_20')
        assert AnalysisDataService.doesExist('pixel_20')
        DeleteWorkspaces(['table_20', 'pixel_20', 'PeakYTable', 'ParametersTable'])  # a bit of clean-up
Пример #7
0
    def test_new_corelli_calibration_and_load_calibration(self):
        r"""Creating a database is time consuming, thus we test both new_corelli_calibration and load_calibration"""
        # populate a calibration database with a few cases. There should be at least one bank with two calibrations
        database = tempfile.TemporaryDirectory()
        cases = [('124016_bank10', '10'), ('124023_bank10', '10'), ('124023_banks_14_15', '14-15')]
        for bank_case, bank_selection in cases:
            # Produce workspace groups 'calibrations', 'masks', 'fits'
            calibrate_banks(self.cases[bank_case], bank_selection)
            masks = 'masks' if AnalysisDataService.doesExist('masks') else None
            save_calibration_set(self.cases[bank_case], database.name, 'calibrations', masks, 'fits')
            DeleteWorkspaces(['calibrations', 'fits'])
            if AnalysisDataService.doesExist('masks'):
                DeleteWorkspaces(['masks'])

        # invoque creation of  new corelli calibration without a date
        calibration_file, mask_file, manifest_file = new_corelli_calibration(database.name)
        for file_path in (calibration_file, mask_file, manifest_file):
            assert path.exists(file_path)
        assert open(manifest_file).read() == 'bankID, timestamp\n10, 20200109\n14, 20200109\n15, 20200109\n'

        # load latest calibration and mask (day-stamp of '124023_bank10' is 20200109)
        calibration, mask = load_calibration_set(self.cases['124023_bank10'], database.name,
                                                 mask_format='TableWorkspace')
        calibration_expected = LoadNexusProcessed(Filename=calibration_file)
        mask_expected = LoadNexusProcessed(Filename=mask_file)
        assert_allclose(calibration.column(1), calibration_expected.column(1), atol=1e-4)
        assert mask.column(0) == mask_expected.column(0)

        # invoque a new corelli calibration with a date falling in between the bank (bank10) in
        # in our small dataset having two calibrations
        calibration_file, mask_file, manifest_file = new_corelli_calibration(database.name, date='20200108')
        for file_path in (calibration_file, mask_file, manifest_file):
            assert path.exists(file_path)
        assert open(manifest_file).read() == 'bankID, timestamp\n10, 20200106\n'

        # load oldest calibration and mask(day-stamp of '124023_bank10' is 20200106)
        calibration, mask = load_calibration_set(self.cases['124016_bank10'], database.name,
                                                 mask_format='TableWorkspace')
        calibration_expected = LoadNexusProcessed(Filename=calibration_file)
        mask_expected = LoadNexusProcessed(Filename=mask_file)
        assert_allclose(calibration.column(1), calibration_expected.column(1), atol=1e-4)
        assert mask.column(0) == mask_expected.column(0)

        database.cleanup()
Пример #8
0
def purge_table(workspace: WorkspaceTypes, calibration_table: TableWorkspace,
                tubes_fit_success: np.ndarray,  output_table: str = None) -> None:
    r"""
    Remove the detectorID's corresponding to the failing tubes from the calibration table

    Assumptions:
    - Each tube has PIXELS_PER_TUBE number of pixels
    - detector ID's in the calibration table are sorted according to tube number

    :param workspace: input Workspace2D containing total neutron counts per pixel
    :param calibration_table: input TableWorkspace containing one column for detector ID and one column
    for its calibrated Y coordinates, in meters
    :param tubes_fit_success: array of booleans of length TUBES_IN_BANK. `False` if a tube was unsuccessfully fitted.
    :param output_table: name of the purged table. If `None`, the input `calibration_table` is purged.
    """
    # validate input arguments
    if False not in tubes_fit_success:
        return  # nothing to do
    # validate the input workspace
    message = f'Cannot process workspace {workspace}. Pass the name of an existing workspace or a workspace handle'
    assert isinstance(workspace, (str, Workspace2D)), message
    workspace_name = str(workspace)
    assert AnalysisDataService.doesExist(workspace_name), f'Input workspace {workspace_name} does not exists'
    # validate the input calibraton table
    message = f'Cannot process table {calibration_table}. Pass the name of an existing TableWorkspace' \
              ' or a TableWorkspace handle'
    assert isinstance(calibration_table, (str, TableWorkspace)), message
    assert AnalysisDataService.doesExist(str(calibration_table)), f'Input table {calibration_table} does not exists'
    if output_table is not None:
        CloneWorkspace(InputWorkspace=calibration_table, OutputWorkspace=output_table)
    else:
        output_table = str(calibration_table)
    tube_fail_indexes = np.where(tubes_fit_success == False)[0]  # noqa E712 indexes of tubes unsuccessfully fitted
    row_indexes_of_first_tube = np.arange(PIXELS_PER_TUBE)  # 0, 1, ... 255
    fail_rows = [row_indexes_of_first_tube + (i * PIXELS_PER_TUBE) for i in tube_fail_indexes]
    fail_rows = np.array(fail_rows, dtype=int).flatten().tolist()
    DeleteTableRows(output_table, fail_rows)
Пример #9
0
    def test_save_calibration_set(self) -> None:
        calibrations, masks = calibrate_banks(self.cases['124023_banks_14_15'], '14-15')
        for w in ('calibrations', 'masks', 'fits'):
            assert AnalysisDataService.doesExist(w)

        # Save everything (typical case)
        database = tempfile.TemporaryDirectory()
        save_calibration_set(self.cases['124023_banks_14_15'], database.name, 'calibrations', 'masks', 'fits')
        for bn in ('014', '015'):  # bank number
            for ct in ('calibration', 'mask', 'fit'):  # table type
                assert path.exists(path.join(database.name, f'bank{bn}', f'{ct}_corelli_bank{bn}_20200109.nxs.h5'))
        database.cleanup()

        #  Save only the calibration tables
        database = tempfile.TemporaryDirectory()
        save_calibration_set(self.cases['124023_banks_14_15'], database.name, 'calibrations')
        for bn in ('014', '015'):  # bank number
            assert path.exists(path.join(database.name, f'bank{bn}', f'calibration_corelli_bank{bn}_20200109.nxs.h5'))
        database.cleanup()

        #  Save only the calibration tables as a list of strings
        database = tempfile.TemporaryDirectory()
        save_calibration_set(self.cases['124023_banks_14_15'], database.name, ['calib14', 'calib15'])
        for bn in ('014', '015'):  # bank number
            assert path.exists(path.join(database.name, f'bank{bn}', f'calibration_corelli_bank{bn}_20200109.nxs.h5'))
        database.cleanup()

        #  Save only the calibration tables as a list of workspaces
        database = tempfile.TemporaryDirectory()
        save_calibration_set(self.cases['124023_banks_14_15'], database.name, [mtd['calib14'], mtd['calib15']])
        for bn in ('014', '015'):  # bank number
            assert path.exists(path.join(database.name, f'bank{bn}', f'calibration_corelli_bank{bn}_20200109.nxs.h5'))
        database.cleanup()

        # Save only one table of each type, passing strings
        database = tempfile.TemporaryDirectory()
        save_calibration_set(self.cases['124023_banks_14_15'], database.name, 'calib14', 'mask14', 'fit14')
        for ct in ('calibration', 'mask', 'fit'):  # table type
            assert path.exists(path.join(database.name, 'bank014', f'{ct}_corelli_bank014_20200109.nxs.h5'))
        database.cleanup()

        # Save only one table of each type, passing workspaces
        database = tempfile.TemporaryDirectory()
        save_calibration_set(self.cases['124023_banks_14_15'], database.name,
                             mtd['calib14'], mtd['mask14'], mtd['fit14'])
        for ct in ('calibration', 'mask', 'fit'):  # table type
            assert path.exists(path.join(database.name, 'bank014', f'{ct}_corelli_bank014_20200109.nxs.h5'))
        database.cleanup()
Пример #10
0
    def test_criterion_peak_vertical_position(self):

        # control bank, it has no problems
        fit_bank(self.cases['123455_bank20'], 'bank20')
        expected = np.ones(16, dtype=bool)
        actual = criterion_peak_vertical_position('PeakYTable', zscore_threshold=2.5, deviation_threshold=0.0035)
        assert_equal(actual, expected)
        DeleteWorkspaces(['CalibTable', 'ParametersTable', 'PeakTable', 'PeakYTable'])  # a bit of clean-up

        # beam center intensity spills over adjacent tubes, tube15 and tube16
        fit_bank(self.cases['123454_bank58'], 'bank58')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], dtype=bool)
        actual = criterion_peak_vertical_position('PeakYTable', zscore_threshold=2.5, deviation_threshold=0.0035)
        assert_equal(actual, expected)
        DeleteWorkspaces(['CalibTable', 'ParametersTable', 'PeakTable', 'PeakYTable'])  # a bit of clean-up

        # tube11 is not working at all
        fit_bank(self.cases['124018_bank45'], 'bank45')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], dtype=bool)
        actual = criterion_peak_vertical_position('PeakYTable', zscore_threshold=2.5, deviation_threshold=0.0035)
        assert_equal(actual, expected)
        DeleteWorkspaces(['CalibTable', 'ParametersTable', 'PeakTable', 'PeakYTable'])  # a bit of clean-up

        # tube 13 has shadows at pixel numbers quite different from the rest, but similar vertical positions
        fit_bank(self.cases['124023_bank10'], 'bank10')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=bool)
        actual = criterion_peak_vertical_position('PeakYTable', zscore_threshold=2.5, deviation_threshold=0.0035)
        assert_equal(actual, expected)
        DeleteWorkspaces(['CalibTable', 'ParametersTable', 'PeakTable', 'PeakYTable'])  # a bit of clean-up

        # one spurious shadow in tube14 throws away the fit
        fit_bank(self.cases['124023_bank15'], 'bank15')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1], dtype=bool)
        actual = criterion_peak_vertical_position('PeakYTable', zscore_threshold=2.5, deviation_threshold=0.0035)
        assert_equal(actual, expected)
        DeleteWorkspaces(['CalibTable', 'ParametersTable', 'PeakTable', 'PeakYTable'])  # a bit of clean-up

        # check for the summary workspace
        fit_bank(self.cases['123455_bank20'], 'bank20')
        criterion_peak_vertical_position('PeakYTable', summary='summary',
                                         zscore_threshold=2.5, deviation_threshold=0.0035)
        assert AnalysisDataService.doesExist('summary')
        workspace = mtd['summary']
        axis = workspace.getAxis(1)
        assert [axis.label(workspace_index) for workspace_index in (0, 1, 2)] == ['success', 'deviation', 'Z-score']
        self.assertEqual(min(workspace.readY(0)), 1.0)  # check success of first tube
        self.assertAlmostEqual(max(workspace.readY(2)), 2.73, delta=0.01)  # check maximum Z-score
        DeleteWorkspaces(['CalibTable', 'ParametersTable', 'PeakTable', 'PeakYTable', 'summary'])  # a bit of clean-up
Пример #11
0
    def test_criterium_peak_pixel_position(self):
        # control bank, it has no problems
        fit_bank(self.cases['123455_bank20'], 'bank20')
        expected = np.ones(16, dtype=bool)
        assert_equal(criterium_peak_pixel_position('PeakTable', zscore_threshold=2.5, deviation_threshold=3), expected)

        # beam center intensity spills over adjacent tubes, tube15 and tube16
        fit_bank(self.cases['123454_bank58'], 'bank58')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], dtype=bool)
        assert_equal(criterium_peak_pixel_position('PeakTable', zscore_threshold=2.5, deviation_threshold=3), expected)

        # tube11 is not working at all
        fit_bank(self.cases['124018_bank45'], 'bank45')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1], dtype=bool)
        assert_equal(criterium_peak_pixel_position('PeakTable', zscore_threshold=2.5, deviation_threshold=3), expected)

        # tube 13 has shadows at pixel numbers quite different from the rest
        fit_bank(self.cases['124023_bank10'], 'bank10')
        expected = np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1], dtype=bool)
        assert_equal(criterium_peak_pixel_position('PeakTable', zscore_threshold=2.5, deviation_threshold=3), expected)

        # tubes 3, 8, and 13 have very faint wire shadows
        fit_bank(self.cases['124023_bank14'], 'bank14')
        expected = np.array([1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1], dtype=bool)
        assert_equal(criterium_peak_pixel_position('PeakTable', zscore_threshold=2.5, deviation_threshold=3), expected)

        # one spurious shadow in tube14, not enough to flag a discrepancy
        fit_bank(self.cases['124023_bank15'], 'bank15')
        expected = np.ones(16, dtype=bool)
        assert_equal(criterium_peak_pixel_position('PeakTable', zscore_threshold=2.5, deviation_threshold=3), expected)

        # check for the summary workspace
        fit_bank(self.cases['123455_bank20'], 'bank20')
        criterium_peak_pixel_position('PeakTable', summary='summary', zscore_threshold=2.5, deviation_threshold=3)
        assert AnalysisDataService.doesExist('summary')
        workspace = mtd['summary']
        axis = workspace.getAxis(1)
        assert [axis.label(workspace_index) for workspace_index in (0, 1, 2)] == ['success', 'deviation', 'Z-score']
        self.assertEqual(min(workspace.readY(0)), 1.0)
        self.assertAlmostEqual(max(workspace.readY(2)), 1.728, delta=0.001)

        DeleteWorkspaces(['CalibTable', 'PeakTable', 'summary'])  # a bit of clean-up
Пример #12
0
 def test_apply_calibration(self) -> None:
     table = calibrate_tube(self.workspace, 'bank42/sixteenpack/tube8')
     apply_calibration(self.workspace, table)
     assert AnalysisDataService.doesExist('uncalibrated_calibrated')
     DeleteWorkspaces(['uncalibrated_calibrated', str(table)])
Пример #13
0
    def test_peak_y_table(self) -> None:
        # Mock PeakTable with two tubes and three peaks. Simple, integer values
        def peak_pixels_table(table_name,
                              peak_count,
                              tube_names=None,
                              pixel_positions=None):
            table = CreateEmptyTableWorkspace(OutputWorkspace=table_name)
            table.addColumn(type='str', name='TubeId')
            for i in range(peak_count):
                table.addColumn(type='float', name='Peak%d' % (i + 1))
            if tube_names is not None and pixel_positions is not None:
                assert len(tube_names) == len(
                    pixel_positions
                ), 'tube_names and pixel_positions have different length'
                for tube_index in range(len(tube_names)):
                    # tube_names is a list of str values; pixel_positions is a list of lists of float values
                    table.addRow([tube_names[tube_index]] +
                                 pixel_positions[tube_index])
            return table

        # Create a peak table with only one tube
        peak_table = peak_pixels_table('PeakTable', 3, ['tube1'], [[0, 1, 2]])

        # Mock ParametersTableGroup with one parameter table. Simple parabola
        def parameters_optimized_table(table_name, values=None, errors=None):
            table = CreateEmptyTableWorkspace(OutputWorkspace=table_name)
            for column_type, column_name in [('str', 'Name'),
                                             ('float', 'Value'),
                                             ('float', 'Error')]:
                table.addColumn(type=column_type, name=column_name)
            if values is not None and errors is not None:
                assert len(values) == 4 and len(
                    errors) == 4  # A0, A1, A2, 'Cost function value'
                for index, row_name in enumerate(
                    ['A0', 'A1', 'A2', 'Cost function value']):
                    table.addRow([row_name, values[index], errors[index]])
            return table

        # Create two tables with optimized polynomial coefficients, then group them
        parameters_optimized_table('parameters_table_0', [0, 0, 1, 1.3],
                                   [0, 0, 0, 0])  # first tube
        parameters_optimized_table('parameters_table_1', [1, 1, 1, 2.3],
                                   [0, 0, 0, 0])  # second tube
        parameters_table = GroupWorkspaces(
            InputWorkspaces=['parameters_table_0', 'parameters_table_1'],
            OutputWorkspace='parameters_table')

        # Check we raise an assertion error since the number of tubes is different than number of tables
        with self.assertRaises(AssertionError) as exception_info:
            calculate_peak_y_table(peak_table,
                                   parameters_table,
                                   output_workspace='PeakYTable')
        assert 'number of rows in peak_table different than' in str(
            exception_info.exception)
        # Add another parameter table to ParametersTableGroup, the create peak_vertical_table
        peak_table.addRow(['tube2', 0, 1, 2])
        table = calculate_peak_y_table(peak_table,
                                       parameters_table,
                                       output_workspace='PeakYTable')
        assert AnalysisDataService.doesExist('PeakYTable')
        assert_allclose(list(table.row(0).values())[1:], [0, 1, 4])
        assert_allclose(list(table.row(1).values())[1:], [1, 3, 7])
Пример #14
0
def fit_bank(workspace: WorkspaceTypes, bank_name: str, shadow_height: float = 1000, shadow_width: float = 4,
             fit_domain: float = 7, minimum_intensity: float = 1000,
             calibration_table: str = 'CalibTable',
             peak_pixel_positions_table: str = 'PeakTable',
             peak_vertical_positions_table: str = 'PeakYTable',
             parameters_table_group: str = 'ParametersTable') -> None:
    r"""
    Find the position of the wire shadow on each tube in the bank, in units of pixel positions

    :param workspace: input Workspace2D containing total neutron counts per pixel
    :param bank_name: a string of the form 'bankI' where 'I' is a bank number
    :param shadow_height: initial guess for the decrease in neutron counts caused by the calibrating wire
    :param shadow_width: initial guess for the number of pixels shadowed by the calibrating wire
    :param fit_domain: number of pixels over which a calibrating wire may have any significant influence
    :param minimum_intensity: mininum number of neutron counts per pixel to warrant a significant fit
    session. This number is compared against the neutron counts per pixel, averaged over all pixels in the bank
    :param calibration_table: output TableWorkspace containing one column for detector ID and one column
    for its calibrated XYZ coordinates, in meters
    :param peak_pixel_positions_table: output table containing the positions of the wire shadows for each tube, in
    units of pixel coordinates
    :param peak_vertical_positions_table: output table containing the positions of the wire shadows for each tube
        along the vertical (Y) axis. This positions are the result if evaluating the peak pixel positions with
        the quadratic function that translates pixel positions to vertical positions (see
        parameters_table_group)
    :param parameters_table_group: name of the WorkspaceGroup containing individual TableWorkspace tables. Each
        table holds values and errors for the optimized coefficients of the polynomial that fits the peak positions (in
        pixel coordinates) to the known wire positions (along the Y-coordinate). The last entry in the table
        holds the goodness-of-fit, chi-square value. The name of each individual TableWorkspace is the string
        `parameters_table_group` plus the suffix `_I`, where `I` is the tube index in the bank, startin at zero.
        If set to `None`, no group workspace is generated.

    :raises AssertionError: the input workspace is of incorrect type or cannot be found
    :raises AssertionError: the input `shadow_height` is a non-positive value
    :raises AssertionError: the `bank_name` if not of the form 'bankI'
    :raises AssertionError: insufficient neutron counts per pixel

    :return: workspace handles to the calibration and peak table
    """
    message = f'Cannot process workspace {workspace}. Pass the name of an existing workspace or a workspace handle'
    assert isinstance(workspace, (str, Workspace2D)), message
    workspace_name = str(workspace)
    assert AnalysisDataService.doesExist(workspace_name), f'Input workspace {workspace_name} does not exists'
    assert shadow_height > 0, 'shadow height must be positive'
    peak_height, peak_width = -shadow_height, shadow_width
    assert re.match(r'^bank\d+$', bank_name), 'The bank name must be of the form "bankI" where "I" in an integer'
    message = f'Insufficient counts per pixel in workspace {workspace_name} for a confident calibration'
    assert sufficient_intensity(workspace, bank_name, minimum_intensity=minimum_intensity), message
    # Fit only the inner 14 dips because the extrema wires are too close to the tube tips.
    # The dead zone in the tube tips interferes with the shadow cast by the extrema  wires
    # preventing a good fitting
    wire_positions_pixels = wire_positions(units='pixels')[1: -1]
    wire_count = len(wire_positions_pixels)
    peaks_form = [1] * wire_count  # signals we'll be fitting dips (peaks with negative heights)

    fit_par = TubeCalibFitParams(wire_positions_pixels, height=peak_height, width=peak_width, margin=fit_domain)
    fit_par.setAutomatic(True)

    tube.calibrate(workspace_name, bank_name, wire_positions(units='meters')[1: -1],
                   peaks_form, fitPar=fit_par, outputPeak=True, parameters_table_group=parameters_table_group)
    if calibration_table != 'CalibTable':
        RenameWorkspace(InputWorkspace='CalibTable', OutputWorkspace=calibration_table)
    trim_calibration_table(calibration_table)  # discard X and Z coordinates
    if peak_pixel_positions_table != 'PeakTable':
        RenameWorkspace(InputWorkspace='PeakTable', OutputWorkspace=peak_pixel_positions_table)
    calculate_peak_y_table(peak_pixel_positions_table, parameters_table_group,
                           output_workspace=peak_vertical_positions_table)