Source code for drtsans.detector
from collections import OrderedDict
import numpy as np
from mantid.kernel import Property
[docs]
class Detector:
r"""
Auxiliary class that has all the information about a detector
It allows to read tube by tube.
"""
def __init__(self, workspace, component_name):
self._workspace = workspace
self._current_start_ws_index = None # first workspace index of the currently considered tube
self._current_stop_ws_index = None # last workspace index of the currently considered tube
self._tube_ws_indices = None # iterator for tube endpoints, given as workspace indexes
# Public variables
self.component_name = None # name of the assembly of pixels making up the Detector
self.n_tubes = None # number of tubes in the detector
self.n_pixels_per_tube = None
self.first_det_id = None # pixel ID for first pixel detector that is not a monitor
self.last_det_id = None # pixel ID for last pixel detector
self.detector_id_to_ws_index = None # mapping from pixel ID to workspace index
self.data_y = None
self.data_e = None
self.tube_ws_indices = None
# Initialize attributes of the Detector
self._detector_details(component_name)
self._detector_id_to_ws_index()
self._extract_data()
self._set_tube_ws_indices()
def _detector_details(self, component_name):
r"""
Initializes the following attributes of the Detector:
component_name, n_tubes, n_pixels_per_tube, first_det_id, last_det_id
Parameters
----------
component_name : string
Name of the pixel assembly, one component of the instrument.
"""
self.component_name = component_name
i = self._workspace.getInstrument()
component = i.getComponentByName(component_name)
num_pixels = 1
# dive into subelements until get a detector
while component.type() != "DetectorComponent" and component.type() != "GridDetectorPixel":
self.n_pixels_per_tube = component.nelements()
num_pixels *= self.n_pixels_per_tube
component = component[0]
self.first_det_id = component.getID()
self.last_det_id = self.first_det_id + num_pixels - 1
self.n_tubes = int(num_pixels / self.n_pixels_per_tube)
def _detector_id_to_ws_index(self):
r"""
Maps the detector ID of one pixel to one workspace index.
Initializes attribute ``detector_id_to_ws_index``.
"""
spectrum_info = self._workspace.spectrumInfo()
detector_id_to_index = []
for ws_index in range(self._workspace.getNumberHistograms()):
if spectrum_info.isMonitor(ws_index) is True:
continue
detector_id_to_index.append((self._workspace.getSpectrum(ws_index).getDetectorIDs()[0], ws_index))
self.detector_id_to_ws_index = OrderedDict(detector_id_to_index)
def _extract_data(self):
r"""
Extract intensitites and associated uncertainties from the workspace.
Initializes attributes ``data_y`` and ``data_e``
"""
self.data_y = self._workspace.extractY()
self.data_e = self._workspace.extractE()
def _set_tube_ws_indices(self):
r"""
Initializes attribute ``_tube_ws_indices`` by assigning to it an iterator that yields pairs of the form
``(start, end)``, where ``start`` and ``end`` are the starting and ending workspace indexes for a given tube.
The iterator yields as many pairs as tubes in Detector.
"""
tube_ws_indices = []
for tube_idx in range(self.n_tubes):
first_det_id = self.first_det_id + tube_idx * self.n_pixels_per_tube # tube starts with this pixel ID
last_det_id = first_det_id + self.n_pixels_per_tube - 1 # tube ends with this pixel ID
first_ws_index = self.detector_id_to_ws_index[first_det_id] # tube starts with this workspace index
last_ws_index = self.detector_id_to_ws_index[last_det_id] # tube ends with this workspace index
tube_ws_indices.append((first_ws_index, last_ws_index))
self._tube_ws_indices = iter(tube_ws_indices) # return an iterator
[docs]
def next_tube(self):
r"""Initializes/ updates attributes ``_current_start_ws_index`` and ``_current_stop_ws_index``"""
self._current_start_ws_index, self._current_stop_ws_index = next(self._tube_ws_indices)
[docs]
def get_current_ws_indices(self):
r"""
First and last workspace indices for the currently considered tube.
Returns
-------
tuple
"""
return self._current_start_ws_index, self._current_stop_ws_index
[docs]
def get_current_ws_indices_range(self):
r"""
Array of workspace indexes for the currently considered tube.
Returns
-------
~numpy.ndarray
"""
return np.array(range(self._current_start_ws_index, self._current_stop_ws_index + 1))
[docs]
def get_ws_data(self):
r"""
Intensities and associated uncertainties for the currently considered tube.
Returns
-------
tuple
A two-item tuple containing, in this order, intensites and uncertainties in the shape of ~numpy.ndarray.
"""
return (
self.data_y[self._current_start_ws_index : self._current_stop_ws_index + 1].flatten(),
self.data_e[self._current_start_ws_index : self._current_stop_ws_index + 1].flatten(),
)
[docs]
def get_pixels_masked(self):
r"""
Pixel masks for the currently considered tube.
Returns
-------
~numpy.ndarray
Array of ``Bool`` values, with :py:obj:`True` for the masked pixels and :py:obj:`False` otherwise.
"""
spectrum_info = self._workspace.spectrumInfo()
return np.array([spectrum_info.isMasked(int(idx)) for idx in self.get_current_ws_indices_range()])
[docs]
def get_pixels_infinite(self):
r"""
Pixel mask for pixels with non-finite intensities in the currently considered tube.
Returns an array of booleans for this tube
where the pixel count is EMPTY_DBL
Returns
-------
~numpy.ndarray
Array of ``Bool`` values, with :py:obj:`True` for the pixels with non-finite intensities, and
:py:obj:`False` otherwise.
"""
return np.array(
[self._workspace.readY(int(idx))[0] == Property.EMPTY_DBL for idx in self.get_current_ws_indices_range()]
)
[docs]
def get_y_coordinates(self):
r"""
Y-coordinates of the pixels for the currently considered tube.
Returns
-------
~numpy.ndarray
"""
detector_info = self._workspace.spectrumInfo()
return np.array([detector_info.position(int(idx))[1] for idx in self.get_current_ws_indices_range()])
[docs]
class Component:
"""
Stores information about the component
"""
# class variables that will cache the component details
dim_x = -1 # number of tubes
dim_y = -1 # number of pixels per tube
dims = -1 # total number of pixels
first_index = -1 # workspace index of the smallest pixel id
def __init__(self, workspace, component_name):
self._component_name = component_name
self._workspace = workspace
self._initialization()
def _initialization(self):
first_det_id = self._detector_details()
self._detector_first_ws_index(first_det_id)
def _num_pixels_in_tube(self, info, component_index):
"""Recursive function that determines how many pixels are in a single
tube (y-dimension). This assumes that things without grand-children
are tubes"""
component_index = int(component_index)
children = info.children(component_index)
grandchildren = info.children(int(children[0]))
if len(grandchildren) == 0:
return children[0], len(children)
else:
return self._num_pixels_in_tube(info, children[0])
def _detector_details(self):
"""Private function that reads the instrument and get component_name
dimensions and first detector id
"""
component_info = self._workspace.componentInfo()
detector_info = self._workspace.detectorInfo()
component_index = component_info.indexOfAny(self._component_name)
total_pixels = len(component_info.detectorsInSubtree(component_index))
tube_index, self.dim_y = self._num_pixels_in_tube(component_info, component_index)
self.dim_x = total_pixels // self.dim_y
self.dims = total_pixels
return detector_info.detectorIDs()[tube_index]
def _detector_first_ws_index(self, first_det_id):
"""sets the first_index of this component"""
for ws_index in range(self._workspace.getNumberHistograms()):
if self._workspace.getSpectrum(ws_index).getDetectorIDs()[0] == first_det_id:
self.first_index = ws_index
break
else:
raise ValueError("Iterared WS and did not find first det id = " "{}".format(first_det_id))
[docs]
def masked_ws_indices(self):
"""
return an array with True or False if a detector is either masked or
not for all the component
Returns
-------
bool np.array
array with True or False if a detector is either masked or
not for all the component
"""
si = self._workspace.spectrumInfo()
mask_array = [si.isMasked(i) for i in range(self.first_index, self.first_index + self.dim_x * self.dim_y)]
return np.array(mask_array)
# TODO - Implement!
[docs]
def monitor_indices(self):
"""
Returns
-------
"""
return np.array([])
def __str__(self):
return "Component: {} with {} pixels (dim x={}, dim y={})." " First index = {}.".format(
self._component_name,
self.dims,
self.dim_x,
self.dim_y,
self.first_index,
)