Source code for drtsans.mask_utils

import numpy as np

r""" Links to Mantid algorithms
ExtractMask          <https://docs.mantidproject.org/nightly/algorithms/ExtractMask-v1.html>
FindDetectorsInShape <https://docs.mantidproject.org/nightly/algorithms/FindDetectorsInShape-v1.html>
LoadMask             <https://docs.mantidproject.org/nightly/algorithms/LoadMask-v1.html>
LoadNexusProcessed   <https://docs.mantidproject.org/nightly/algorithms/LoadNexusProcessed-v2.html>
MaskAngle            <https://docs.mantidproject.org/nightly/algorithms/MaskAngle-v1.html>
MaskBTP              <https://docs.mantidproject.org/nightly/algorithms/MaskBTP-v1.html>
MaskDetectors        <https://docs.mantidproject.org/nightly/algorithms/MaskDetectors-v1.html>
MaskSpectra          <https://docs.mantidproject.org/nightly/algorithms/MaskSpectra-v1.html>
"""
from mantid.simpleapi import (
    ExtractMask,
    FindDetectorsInShape,
    LoadMask,
    logger,
    MaskBTP,
    MaskDetectors,
    MaskSpectra,
    LoadNexusProcessed,
    MaskAngle,
)
from mantid.api import mtd, MatrixWorkspace, IEventWorkspace
import os


__all__ = ["apply_mask", "circular_mask_from_beam_center"]


def mask_as_numpy_array(input_workspace, invert=False):
    r"""
    Array of mask or roi (region-of-interest) boolean values for the pixel detectors.

    When ``invert=False``, a ``True`` value indicates a masked pixel detector. When ``invert=True``, a ``True``
    value indicates an unmasked pixel detector. Option ``invert=True`` in indicated when working with
    region-of-interest areas of the detector.

    Parameters
    ----------
    input_workspace: str, , ~mantid.api.MatrixWorkspace, ~mantid.api.IEventWorkspace
    invert: bool
        Invert mask values.

    Returns
    -------
    :ref:`~numpy.ndarray`
        Array of boolean values, with ``True`` representing masked spectra.
    """
    input_workspace = mtd[str(input_workspace)]  # handle to the workspace
    mask = [input_workspace.getDetector(i).isMasked() for i in range(input_workspace.getNumberHistograms())]
    mask = np.asarray(mask)
    return mask if invert is False else np.invert(mask)


def masked_indexes(input_workspace, invert=False):
    r"""
    Return indexes for either masked or unmasked pixel detectors.

    When ``invert=False``, indexes for all masked pixel detectors are returned. When ``invert=True``, indexes for
    all un masked pixel detectors are returned. Option ``invert=True`` in indicated when working with
    region-of-interest areas of the detector.

    Parameters
    ----------
    input_workspace: str, , ~mantid.api.MatrixWorkspace, ~mantid.api.IEventWorkspace
    invert: bool
        return indexes for unmasked pixel detectors.

    Returns
    -------
    :ref:`~numpy.ndarray`
        Array of integers
    """
    mask_array = mask_as_numpy_array(input_workspace, invert=invert)
    return np.where(mask_array)[0]


[docs] def apply_mask(input_workspace, mask=None, panel=None, **btp): r""" Apply a mask to a workspace. The function accepts a path to a mask file, a MaskWorkspace, or options to algorithm :ref:`MaskBTP <algm-MaskBTP-v1>`. Parameters ---------- input_workspace: str, ~mantid.api.IEventWorkspace, ~mantid.api.MatrixWorkspace Workspace to be masked mask: mask file path, ~mantid.api.MaskWorkspace, :py:obj:`list` Additional mask to be applied. If :py:obj:`list`, it is a list of detector ID's. If `None`, it is expected that `maskbtp` is not empty. panel: str Either 'front' or 'back' to mask a whole panel btp: dict Options to Mantid algorithm :ref:`MaskBTP <algm-MaskBTP-v1>` or :ref:`MaskAngle <algm-MaskAngle-v1>`. Will be used if ``mask=None`` """ input_workspace = str(input_workspace) # instrument = mtd[input_workspace].getInstrument().getName() if mask is not None: if isinstance(mask, str): if os.path.splitext(mask)[1] == ".xml": # mask_workspace = LoadMask(Instrument=instrument, InputFile=mask, # RefWorkspace=input_workspace, # OutputWorkspace=mtd.unique_hidden_name()) mask_workspace = load_mask_xml(mask, input_workspace) else: mask_workspace = load_mask(mask) MaskDetectors(Workspace=input_workspace, MaskedWorkspace=mask_workspace) mask_workspace.delete() # delete temporary workspace elif isinstance(mask, MatrixWorkspace): MaskDetectors(Workspace=input_workspace, MaskedWorkspace=mask) elif isinstance(mask, list): MaskDetectors(Workspace=input_workspace, DetectorList=mask) if panel: MaskBTP(Workspace=input_workspace, Components=panel + "-panel") if bool(btp): min_angle = btp.pop("MinAngle", None) max_angle = btp.pop("MaxAngle", None) angle = btp.pop("Angle", "TwoTheta") if min_angle is not None or max_angle is not None: MaskAngle( Workspace=input_workspace, MinAngle=min_angle, MaxAngle=max_angle, Angle=angle, ) if bool(btp): # see if any parameters are left print("Try to mask BTP to workspace {} with {}".format(input_workspace, btp)) MaskBTP(Workspace=input_workspace, **btp)
def load_mask(mask_file="", output_workspace=None): r""" Load mask file in a workspace Parameters ---------- mask_file: mask file path path to mask file output_workspace: Name of the output ~mantid.api.MatrixWorkspace. If ``None``, a random name will be provided for the workspace. Returns -------- str, ~mantid.api.MatrixWorkspace Workspace """ if not output_workspace: output_workspace = mtd.unique_hidden_name() mask_workspace = LoadNexusProcessed(Filename=mask_file, OutputWorkspace=output_workspace) if isinstance(mask_workspace, IEventWorkspace): logger.warning( "Storing the mask on an EventWorkspace is inefficient. \ Consider saving as a histogram with one bin." ) return mask_workspace def load_mask_xml(mask_file, ref_workspace, output_workspace=None): """Load mask file in a workspace Parameters ---------- mask_file ref_workspace output_workspace Returns ------- """ # Create output MaskWorkspace name if not specified if not output_workspace: output_workspace = mtd.unique_hidden_name() # Get instrument name ref_workspace = str(ref_workspace) instrument = mtd[ref_workspace].getInstrument().getName() # Load mask_workspace = LoadMask( Instrument=instrument, InputFile=mask_file, RefWorkspace=ref_workspace, OutputWorkspace=output_workspace, ) return mask_workspace def mask_spectra_with_special_values(input_workspace, output_workspace=None): r""" Mask spectra in a workspace containing non-finite values. Non-finite values are evaluated with `numpy.isfinite` Parameters ---------- input_workspace: str, MatrixWorkspace special_values: list List of string representations for special `float` values. The special value can be obtained by applying `float` to the string, e.g. float('nan'). output_workspace : str Name of the normalized workspace. If None, the name of the input workspace is chosen (the input workspace is overwritten). Returns ------- list Workspace indexes masked. Returns zero if no spectra are masked. """ if output_workspace is None: output_workspace = str(input_workspace) workspace = mtd[str(input_workspace)] intensities = workspace.extractY() non_finite_indexes = np.argwhere( np.isfinite(np.sum(intensities, axis=-1)) == False # NOTE: this is a awkward way to do it ) # noqa: E712 non_finite_indexes = non_finite_indexes.flatten().tolist() if len(non_finite_indexes) > 0: MaskSpectra( InputWorkspace=input_workspace, InputWorkspaceIndexType="WorkspaceIndex", InputWorkspaceIndexSet=non_finite_indexes, OutputWorkspace=output_workspace, ) return len(non_finite_indexes)
[docs] def circular_mask_from_beam_center(input_workspace, radius, unit="mm"): """ Find the detectors ID's within a certain radius from the beam center Parameters ---------- input_workspace: MatrixWorkspace Workspace containing the detector already beam-centered radius: float Radius of the circle encompassing the detectors of interest. unit: str Either 'mm' or 'm', unit of the `radius` option. Returns ------- numpy.ndarray List of detector ID's """ r = radius * 1e-3 if unit == "mm" else radius cylinder = r""" <infinite-cylinder id="shape"> <centre x="0.0" y="0.0" z="0.0" /> <axis x="0.0" y="0.0" z="1" /> <radius val="{}" /> </infinite-cylinder> <algebra val="shape" /> """.format( r ) det_ids = FindDetectorsInShape(Workspace=input_workspace, ShapeXML=cylinder) return det_ids
def masked_detectors(input_workspace, query_ids=None): r""" List of detector ID's that are masked Parameters ---------- input_workspace: str, MatrixWorkspace Input workspace to find the detectors query_ids: list Restrict the search to this list of detector ID's. If `None`, query all detectors. Returns ------- list """ mask_ws, det_ids = ExtractMask(input_workspace, OutputWorkspace=mtd.unique_hidden_name()) if query_ids is not None: det_ids = sorted(list(set(det_ids) & set(query_ids))) mask_ws.delete() return det_ids