Unverified Commit d99b0318 authored by Jose Borreguero's avatar Jose Borreguero Committed by GitHub
Browse files

Merge pull request #31093 from mantidproject/mask_as_workspace

Convert TableWorkspace to MaskWorkspace
parents f6d307cf ec8af979
......@@ -20,6 +20,10 @@ Algorithms
- :ref:`CompareWorkspaces <algm-CompareWorkspaces>` compares the positions of both source and sample (if extant) when property `checkInstrument` is set.
- :ref:`SetGoniometer <algm-SetGoniometer>` can now set multiple goniometers from log values instead of just the time-avereged value.
Improvements
------------
- Loading a CORELLI tube calibration returns a ``MaskWorkspace``.
Data Objects
------------
......
......@@ -13,10 +13,11 @@ import pathlib
import re
from typing import List, Optional, Tuple, Union
from mantid.dataobjects import EventWorkspace, TableWorkspace, Workspace2D
from mantid.dataobjects import EventWorkspace, MaskWorkspace, TableWorkspace, Workspace2D
from mantid.api import mtd, Workspace, WorkspaceGroup
from mantid.kernel import logger
from mantid.simpleapi import CreateEmptyTableWorkspace, SaveNexusProcessed, LoadNexusProcessed
from mantid.simpleapi import (ClearMaskFlag, CreateEmptyTableWorkspace, ExtractMask, LoadEmptyInstrument,
LoadNexusProcessed, MaskDetectors, SaveNexusProcessed)
# Functions exposed to the general user (public) API
__all__ = ['day_stamp', 'load_calibration_set', 'new_corelli_calibration', 'save_calibration_set']
......@@ -454,10 +455,34 @@ def new_corelli_calibration(database_path: str,
return [file_paths[x] for x in ('calibration', 'mask', 'manifest')]
def _table_to_workspace(input_workspace: Union[str, TableWorkspace],
output_workspace: Optional[str] = None) -> MaskWorkspace:
r"""
@brief Convert a CORELLI calibration mask table to a MaskWorkspace
:param input_workspace : the table containing the detector ID's for the masked detectors
:param output_workspace : name of the output MaskWorkspace
:return: handle to the MaskWorkspace
"""
table_handle = mtd[str(input_workspace)]
detectors_masked = table_handle.column(0) # list of masked detectors
if output_workspace is None:
output_workspace = str(input_workspace)
LoadEmptyInstrument(InstrumentName='CORELLI', OutputWorkspace=output_workspace)
ClearMaskFlag(Workspace=output_workspace) # for good measure
MaskDetectors(Workspace=output_workspace, DetectorList=detectors_masked) # output_workspace is a Workspace2D
# output_workspace is converted to a MaskWorkspace, where the Y-values of the spectra are now either 0 or 1
ExtractMask(InputWorkspace=output_workspace, OutputWorkspace=output_workspace)
return mtd[output_workspace]
def load_calibration_set(input_workspace: Union[str, Workspace],
database_path: str,
output_calibration_name: str = 'calibration',
output_mask_name: str = 'mask') -> Tuple[Optional[TableWorkspace], Optional[TableWorkspace]]:
output_mask_name: str = 'mask',
mask_format: str = 'MaskWorkspace') -> Tuple[Optional[TableWorkspace],
Optional[TableWorkspace]]:
r"""
Retrieve an instrument calibration and instrument mask.
......@@ -469,9 +494,15 @@ def load_calibration_set(input_workspace: Union[str, Workspace],
:param database_path: absolute path to the instrument calibration tables
:param output_calibration_name: name of the TableWorkspace containing the calibrated pixel positions
:param output_mask_name: name of the TableWorkspace containing the uncalibrated pixels to be masked
:return: calibration and mask tables. Returns `None` for each of these tables if a suitable
:param mask_format: return the mask either as a 'MaskWorkspace' or as 'TableWorkspace/
:return: calibration TableWorkspdce and mask MaskWorkspace. Returns `None` for each of these if a suitable
calibration file is not found in the database.
"""
valid_mask_formats = ('MaskWorkspace', 'TableWorkspace')
if mask_format not in valid_mask_formats:
raise ValueError(f'mask_format must be one of {valid_mask_formats}')
workspace_names = {'calibration': output_calibration_name, 'mask': output_mask_name}
run_start = day_stamp(input_workspace) # day when the experiment associated to `input_workspace` was started
instrument_tables = {'calibration': None, 'mask': None} # store the calibration and mask instrument tables
......@@ -502,6 +533,9 @@ def load_calibration_set(input_workspace: Union[str, Workspace],
logger.notice(f'Found {filename} for {str(input_workspace)} with run start {run_start}')
instrument_tables[table_type] = LoadNexusProcessed(Filename=filename,
OutputWorkspace=workspace_names[table_type])
# Additional step to convert the mask TableWorkspace to a MaskWorkspace
if table_type == 'mask' and mask_format=='MaskWorkspace':
instrument_tables[table_type] = _table_to_workspace(workspace_names[table_type])
else:
message = f'No {table_type} file found for {str(input_workspace)} with run start {run_start}. '
if len(available_dates) > 0:
......
......@@ -8,6 +8,7 @@
from contextlib import contextmanager
from datetime import datetime
import numpy as np
from numpy.testing import assert_allclose
from os import path, remove
import pathlib
......@@ -18,13 +19,14 @@ import unittest
from mantid import AnalysisDataService, config
from mantid.api import mtd, WorkspaceGroup
from mantid.dataobjects import TableWorkspace
from mantid.simpleapi import CreateEmptyTableWorkspace, CreateSampleWorkspace, DeleteWorkspaces, LoadNexusProcessed
from mantid.dataobjects import MaskWorkspace, TableWorkspace
from mantid.simpleapi import (CreateEmptyTableWorkspace, CreateSampleWorkspace, DeleteWorkspace, DeleteWorkspaces,
LoadNexusProcessed)
from corelli.calibration.database import (combine_spatial_banks, combine_temporal_banks, day_stamp, filename_bank_table,
has_valid_columns, init_corelli_table, load_bank_table, load_calibration_set,
new_corelli_calibration, save_bank_table, save_calibration_set,
save_manifest_file, verify_date_format)
save_manifest_file, _table_to_workspace, verify_date_format)
from corelli.calibration.bank import calibrate_banks
......@@ -52,7 +54,6 @@ class TestCorelliDatabase(unittest.TestCase):
cls.workspaces_temporary.append(workspace)
def setUp(self) -> None:
# create a mock database
# tests save_bank_table and load_bank_table, save_manifest
self.database_path: str = TestCorelliDatabase.test_dir.name
......@@ -102,7 +103,6 @@ class TestCorelliDatabase(unittest.TestCase):
self.ws_group.addWorkspace(load_bank_table(40, self.database_path, '20200601'))
def test_init_corelli_table(self):
corelli_table = init_corelli_table()
assert isinstance(corelli_table, TableWorkspace)
......@@ -282,7 +282,7 @@ class TestCorelliDatabase(unittest.TestCase):
self.assertAlmostEqual(expected_array, table_dict['Detector Y Coordinate'][i])
def test_new_corelli_calibration_and_load_calibration(self):
r"""Creating a database is time consuming, thus we test both new_corelli_clibration and load_calibration"""
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')]
......@@ -302,7 +302,8 @@ class TestCorelliDatabase(unittest.TestCase):
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)
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)
......@@ -316,7 +317,8 @@ class TestCorelliDatabase(unittest.TestCase):
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)
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)
......@@ -324,6 +326,28 @@ class TestCorelliDatabase(unittest.TestCase):
database.cleanup()
def test_table_to_workspace(self) -> None:
r"""Test the conversion of a TableWorkspace containing the masked detector ID's to a MaskWorkspace object"""
output_workspace = 'test_table_to_workspace_masked'
# Have a fake mask table, masking bank 42
mask_table = CreateEmptyTableWorkspace(OutputWorkspace=output_workspace)
mask_table.addColumn(type='int', name='Detector ID')
begin, end = 167936, 172030 # # Bank 42 has detector ID's from 167936 to 172030
for detector_id in range(begin, 1 + end):
mask_table.addRow([detector_id])
# Convert to MaskWorkspace
mask_table = _table_to_workspace(mask_table)
# Check the output workspace is of type MaskWorkspace
assert isinstance(mask_table, MaskWorkspace)
# Check the output workspace has 1 on workspace indexes for bank 42, and 0 elsewhere
mask_flags = mask_table.extractY().flatten()
offset = 3 # due to the detector monitors, workspace_index = detector_id + offset
masked_workspace_indexes = slice(begin + offset, 1 + end + offset)
assert np.all(mask_flags[masked_workspace_indexes]) # all values are 1
mask_flags = np.delete(mask_flags, masked_workspace_indexes)
assert not np.any(mask_flags) # no value is 1
DeleteWorkspace(output_workspace)
def test_load_calibration_set(self) -> None:
r"""
1. create an empty "database"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment