diff --git a/docs/src/StrixelPixelRemapping/PredefinedMaps.rst b/docs/src/StrixelPixelRemapping/PredefinedMaps.rst index 6ee5645f..8d3906e8 100644 --- a/docs/src/StrixelPixelRemapping/PredefinedMaps.rst +++ b/docs/src/StrixelPixelRemapping/PredefinedMaps.rst @@ -1,6 +1,6 @@ .. _map_generators: -Predefined Map Generators +Predefined Strixel to Pixel Maps ================================ Includes predefined pixel to strixel maps using the sensor configurations introduced in :ref:`predefined_sensor_configs`. @@ -8,38 +8,20 @@ Includes predefined pixel to strixel maps using the sensor configurations introd An introduction to the concept of strixel-to-pixel remapping and and overview of the corresponding API can be found in :ref:`strixel_remapping_index`. -Predefined Maps for 2x2 cm iLGAD Sensor +Predefined Map for 2x2 cm iLGAD Sensor ---------------------------------------- -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_singlechip_25um_strixel_map +.. doxygenclass:: aare::remap::Jungfrau_iLGAD_StrixelPixelMap + :members: -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_singlechip_15um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_singlechip_18um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_singlechip_multipitch_strixel_maps(InclusiveROI, defs::SensorModulePlacement, defs::BondShift) - - -Predefined Maps for 2x2 cm TEW Sensor +Predefined Map for 2x2 cm TEW Sensor ---------------------------------------- -.. doxygenfunction:: aare::remap::generate::jungfrau_tew_singlechip_25um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_tew_singlechip_15um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_tew_singlechip_18um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_tew_singlechip_multipitch_strixel_maps(InclusiveROI, defs::SensorModulePlacement, defs::BondShift) - +.. doxygenclass:: aare::remap::Jungfrau_TEW_StrixelPixelMap + :members: Predefined Maps for 4x4 cm iLGAD Sensor ---------------------------------------- -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_quadbottom_25um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_quadtop_25um_strixel_map - -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_quad_25um_strixel_maps - -.. doxygenfunction:: aare::remap::generate::jungfrau_ilgad_quad_25um_strixel_map - +.. doxygenclass:: aare::remap::Jungfrau_iLGAD_Quad_StrixelPixelMap + :members: \ No newline at end of file diff --git a/docs/src/StrixelPixelRemapping/StrixelPixelRemapAlgorithm.rst b/docs/src/StrixelPixelRemapping/StrixelPixelRemapAlgorithm.rst index 9cbb0f01..0456b035 100644 --- a/docs/src/StrixelPixelRemapping/StrixelPixelRemapAlgorithm.rst +++ b/docs/src/StrixelPixelRemapping/StrixelPixelRemapAlgorithm.rst @@ -1,15 +1,16 @@ .. _remap_algorithm: Core Remapping Algorithm -==================================== +======================== An introduction to the concept of strixel-to-pixel remapping and and overview of the corresponding API can be found in :ref:`strixel_remapping_index`. -.. doxygenfunction:: aare::remap::algo::strixel_to_pixel_map -.. doxygenfunction:: aare::remap::algo::strixel_to_pixel_maps +.. doxygenclass:: aare::remap::StrixelPixelMap + :members: + :undoc-members: -.. doxygenfunction:: aare::remap::algo::ApplyRemap + diff --git a/docs/src/StrixelPixelRemapping/index.rst b/docs/src/StrixelPixelRemapping/index.rst index 33a4683d..a4c5722c 100644 --- a/docs/src/StrixelPixelRemapping/index.rst +++ b/docs/src/StrixelPixelRemapping/index.rst @@ -96,14 +96,14 @@ API Overview .. admonition:: Core Concept of the API - Define a :code:`SensorConfig` that can contain multiple (N) contiguous strixel groups. - Provide user ROI (defining the pixel reference grid), sensor placement on module, and bond shift if applicable. - Hand these inputs to the core remapping algorithm. It produces the separate strixel-to-pixel maps for each strixel group on the sensor. - Using the method :code:`ApplyRemap()`, the map can be applied to an input frame. - For convenience, the API provides **predefined map generators** using **predefined sensor configurations**. + Define a :code:`SensorConfig` that can contain multiple (N) contiguous strixel groups and the sensor size. + Create a :code:`StrixelPixelMap` from the sensor configuration, its placement and rotation on the module :code:`SensorModulePlacement` and bond shift if applicable. + You can calculate the strixel-to-pixel map by passing a user ROI to the member function :code:`calculate_map()`. + Passing an input to the :code:`StrixelPixelMap` call operator returns the remapped output frame. + For convenience, the API provides **predefined StrixelPixelMap** using **predefined sensor configurations** for the most common senor configurations at the slsDetectorGroup. API Documentation ----------------- +------------------ .. toctree:: :maxdepth: 1 diff --git a/docs/src/python/file/pyClusterFile.rst b/docs/src/python/file/pyClusterFile.rst index d6f3c8b9..b406cab3 100644 --- a/docs/src/python/file/pyClusterFile.rst +++ b/docs/src/python/file/pyClusterFile.rst @@ -3,7 +3,7 @@ ClusterFile =========== The :func:`ClusterFile` factory is the main interface for reading and writing - cluster files. Use ``mode="r"`` to read, ``mode="w"`` to truncate and +cluster files. Use ``mode="r"`` to read, ``mode="w"`` to truncate and write, or ``mode="a"`` to append. The format does not store the cluster dimensions, value type, coordinate type, diff --git a/docs/src/python/strixelremap/index.rst b/docs/src/python/strixelremap/index.rst index a193d661..c7741b59 100644 --- a/docs/src/python/strixelremap/index.rst +++ b/docs/src/python/strixelremap/index.rst @@ -1,16 +1,87 @@ -Pixel to Strixel Remapping +.. _py_strixel_remapping_index: + +Strixel to Pixel Remapping ============================ +.. + maybe document what a strixel is in here how the remapping roughly works what types are predefined -.. toctree:: - :caption: Pixel to Strixel Remapping - :maxdepth: 1 +Here the concepts behind the pixel reordering ("remapping") that needs to be applied for detectors using sensors +with "strixel" geometry and the corresponding API interface are explained. - pySensorConfiguration - pyStrixelPixelRemapAlgorithm - pyInclusiveROI - pyPredefinedMaps - pyPredefinedSensorConfigs +The Strixel Concept +---------------------- + +A *strixel* is a rectangular sensor pixel. + +The strixel's long edge is an integer multiple of the square pixel pitch +of the ASIC to which the sensor is coupled (i.e. 75 µm for the JUNGFRAU ASIC and 25 µm for MÖNCH). Its short edge, +in turn, is a fraction of the ASIC pixel pitch by the same integer. + +.. IMPORTANT:: + The integer value that defines the strixel dimensioning with respect to the ASIC pixel is called the **strixel multiplicity** :math:`m`. + +.. math:: + \begin{equation*} + \Delta x_{\text{strixel}} = \Delta x_{\text{pixel}}\cdot m \quad \quad \Delta y_{\text{strixel}} = \frac{\Delta y_{\text{pixel}}}{m} + \end{equation*} + +.. figure:: ../../../figures/StrixelSketch_symbols.png + :target: ../../../figures/StrixelSketch_symbols.png + :width: 650px + :align: center + :alt: Strixel Sketch + + Example snippet of a strixel sensor layout with strixel multiplicity :math:`m = 3`. + +.. admonition:: Physics Background + + Rectangular pixels provide a short pitch in one dimension that enables interpolation based on charge sharing + while retaining compatibility with established readout ASICs such as JUNGFRAU and MÖNCH. Strixel sensors are + especially important for detector applications such as **Resonant Inelastic X-ray Scattering (RIXS)** that require + high spatial resolution in one dimension. + +.. admonition:: Fun Fact + + *"Strixel"* is a mashup between the words *"strip"* and *"pixel"*. The alternative version *"stripsel"* is also popular. + +The Remapping Concept +------------------------- + +Each strixel on a sensor is routed to a pixel on the readout ASIC. This reshuffles the order of the sensor strixels into the order +of the square ASIC pixels. In order to reproduce the real physical image on the sensor, we need to provide a map that uniquely +links each physical sensor strixel to its corresponding ASIC pixel. We refer to this procedure as "remapping". + +.. figure:: ../../../figures/StrixelRemappingConcept.png + :target: ../../../figures/StrixelRemappingConcept.png + :width: 650px + :align: center + :alt: Strixel Remapping Concept + + Illustrating the remapping between an ASIC pixel grid and a sensor strixel grid with :math:`m = 3`. + +.. IMPORTANT:: + The remapping algorithm essentially reorders chunks of ASIC pixel columns into chunks of strixel rows based + on the strixel multiplicity. + +The resulting map describes + +.. math:: + \begin{equation*} + \text{strixel}(\text{row},\,\text{col}) \rightarrow \text{flattened ASIC pixel index} = y_{\text{pixel}}\cdot n_x + x_{\text{pixel}} + \end{equation*} + +where :math:`x_{\text{pixel}}` and :math:`y_{\text{pixel}}` are the x- and y-coordinates (column and row) of the reference ASIC pixel, +respectively, and :math:`n_x` is the total number of ASIC pixel columns of the reference pixel grid. + +.. Note:: + The reference pixel grid does not necessarily have to correspond to exactly one ASIC. It could, for example, be the + standard output of a single JUNGFRAU detector module with dimensions :math:`2\times 4` ASICs (:math:`512\times 1024` pixels) + or any other user-chosen ROI. + +.. IMPORTANT:: + To generate a correct strixel-to-pixel map, the relative location of the contiguous strixel region on the sensor with respect + to the reference pixel grid has to be known. Example Usage @@ -30,14 +101,44 @@ Example Usage rx_roi = frame.master().rois[0] # Get ROI of the frame # Get the pixel to strixel map for the 25 µm pitch strixels placed on Chip1 - pixel_to_strixel_map_25um = strixelremap.jungfrau_ilgad_singlechip_25um_strixel_map(user_roi = strixelremap.toInclusiveROI(rx_roi), placement = strixelremap.Chip1) + pixel_to_strixel_map_25um = strixelremap.Jungfrau_iLGAD_StrixelPixelMap(placement = strixelremap.Chip1) + + # calculate the map for the given ROI + pixel_to_strixel_map_25um.calculate_map(rx_roi) + + # apply map + mapped_strixels = pixel_to_strixel_map_25um(frame) + +Alternatively, one can directly pass the map to the :code:`RawFile` reader to automatically remap the strixels when reading the frame: + +.. code:: python + + from aare import strixelremap + import numpy as np + + from aare import RawFile + + # Get the pixel to strixel map for the 25 µm pitch strixels placed on Chip1 + pixel_to_strixel_map_25um = strixelremap.Jungfrau_iLGAD_StrixelPixelMap(placement = strixelremap.Chip1) + + file = RawFile("path/to/master_file.json") # Load a raw file with strixel data + + with RawFile("path/to/master_file.json", strixeltransform = [pixel_to_strixel_map_25um]) as f: + header, frames = f.read_frame() + +API Documentation +------------------ + +.. toctree:: + :caption: Pixel to Strixel Remapping + :maxdepth: 1 + + pySensorConfiguration + pyStrixelPixelRemapAlgorithm + pyInclusiveROI + pyPredefinedMaps + pyPredefinedSensorConfigs - # map(row, col) gives flattened index of the pixel mapped to strixel at (row, col) - order_map = pixel_to_strixel_map_25um.map - strixels = np.empty(order_map.shape, dtype=np.uint16) - - # Apply the remapping to a given strixel data array - strixelremap.apply_remap(frame.astype(np.uint16), order_map, strixels) \ No newline at end of file diff --git a/docs/src/python/strixelremap/pyPredefinedMaps.rst b/docs/src/python/strixelremap/pyPredefinedMaps.rst index d54bdf1f..5e792ed7 100644 --- a/docs/src/python/strixelremap/pyPredefinedMaps.rst +++ b/docs/src/python/strixelremap/pyPredefinedMaps.rst @@ -3,38 +3,30 @@ Predefined Pixel to Strixel Maps Includes predefined pixel to strixel maps for the sensor configurations introduced in :ref:`python_predefined_sensor_configs`. -Predefined Maps for 2x2 cm iLGAD Sensor +Predefined Map for 2x2 cm iLGAD Sensor ---------------------------------------- .. py:currentmodule:: aare.strixelremap -.. autofunction:: jungfrau_ilgad_singlechip_25um_strixel_map - -.. autofunction:: jungfrau_ilgad_singlechip_15um_strixel_map - -.. autofunction:: jungfrau_ilgad_singlechip_18um_strixel_map - -.. autofunction:: jungfrau_ilgad_strixel_maps +.. autoclass:: Jungfrau_iLGAD_StrixelPixelMap + :special-members: __init__, __call__ + :members: + :inherited-members: -Predefined Maps for 2x2 cm TEW Sensor +Predefined Map for 2x2 cm TEW Sensor ---------------------------------------- -.. autofunction:: jungfrau_tew_singlechip_25um_strixel_map - -.. autofunction:: jungfrau_tew_singlechip_15um_strixel_map - -.. autofunction:: jungfrau_tew_singlechip_18um_strixel_map - -.. autofunction:: jungfrau_tew_strixel_maps - +.. autoclass:: Jungfrau_TEW_StrixelPixelMap + :special-members: __init__, __call__ + :members: + :inherited-members: Predefined Maps for 4x4 cm iLGAD Sensor ---------------------------------------- -.. autofunction:: jungfrau_ilgad_quadbottom_25um_strixel_map - -.. autofunction:: jungfrau_ilgad_quadtop_25um_strixel_map - -.. autofunction:: jungfrau_ilgad_quad_25um_strixel_map +.. autoclass:: Jungfrau_iLGAD_Quad_StrixelPixelMap + :special-members: __init__ + :members: + :inherited-members: diff --git a/docs/src/python/strixelremap/pySensorConfiguration.rst b/docs/src/python/strixelremap/pySensorConfiguration.rst index 046d2158..f094fce2 100644 --- a/docs/src/python/strixelremap/pySensorConfiguration.rst +++ b/docs/src/python/strixelremap/pySensorConfiguration.rst @@ -1,7 +1,11 @@ Sensor configurations ===================== -Helper structs to define the sensor configuration, such as sensor pixel geometry, placement and rotation of the sensor on the module, the different strixel types on the sensor and its placement. +Helper structs for strong-typing to define the sensor configuration, such as sensor pixel geometry, placement and rotation of the sensor +on the module, the different strixel types on the sensor and its placement. + +An introduction to the concept of strixel-to-pixel remapping and and overview of the corresponding API can be found +in :ref:`py_strixel_remapping_index`. .. py:currentmodule:: aare.strixelremap @@ -12,6 +16,7 @@ Helper structs to define the sensor configuration, such as sensor pixel geometry :members: .. autoclass:: BondShift + :noindex: :members: :special-members: __init__ @@ -49,9 +54,9 @@ Helper structs to define the sensor configuration, such as sensor pixel geometry .. autofunction:: SensorConfig :noindex: - .. code:: python from aare import strixelremap - SensorConfig(strixelremap.SingleChipMP_iLGAD_pix, [strixelremap.StrxP15, strixelremap.StrxP25]) # returns a SensorConfig_2PixelGroups \ No newline at end of file + strixelremap.SensorConfig(strixelremap.SingleChipMP_iLGAD_pix, [strixelremap.StrxP15, strixelremap.StrxP25]) # returns a SensorConfig_2PixelGroups + diff --git a/docs/src/python/strixelremap/pyStrixelPixelRemapAlgorithm.rst b/docs/src/python/strixelremap/pyStrixelPixelRemapAlgorithm.rst index 24cd0ecf..63a7b21c 100644 --- a/docs/src/python/strixelremap/pyStrixelPixelRemapAlgorithm.rst +++ b/docs/src/python/strixelremap/pyStrixelPixelRemapAlgorithm.rst @@ -3,10 +3,23 @@ Strixel to Pixel Remapping .. py:currentmodule:: aare.strixelremap -.. autofunction:: strixel_to_pixel_map -.. autofunction:: strixel_to_pixel_maps - :noindex: +**Helper functions to create StrixelPixelMap from SensorConfig with N pixel groups:** -.. autofunction:: apply_remap +.. autofunction:: StrixelPixelMap + :noindex: +.. code:: python + + from aare import strixelremap + + strixelpixelmap = strixelremap.StrixelPixelMap(strixelremap.SensorConfig(strixelremap.SingleChipMP_iLGAD_pix, [strixelremap.StrxP25]), strixelremap.Chip1, strixelremap.BondShift(2, 2)) # returns a StrixelPixelMap for the custom SensorConfig with Sensored placed on Chip1 and a BondShift of 2 pixels in x and y direction + + + +**Example of the full StrixelPixelRemap class for a SensorConfig with 3 pixel groups:** + +.. autoclass:: StrixelPixelMap_3Groups_3Maps + :special-members: __init__, __call__ + :members: + :inherited-members: diff --git a/python/src/StrixelRemap/bind_InclusiveROI.hpp b/python/src/StrixelRemap/bind_InclusiveROI.hpp index 0217ed0a..5f01e241 100644 --- a/python/src/StrixelRemap/bind_InclusiveROI.hpp +++ b/python/src/StrixelRemap/bind_InclusiveROI.hpp @@ -57,6 +57,7 @@ void define_InclusiveROI(py::module &m) { }, R"( check if the ROI fits within a given number of columns and rows + Parameters ---------- ncols : int diff --git a/python/src/StrixelRemap/bind_StrixelPixelMapDefs.hpp b/python/src/StrixelRemap/bind_StrixelPixelMapDefs.hpp index 3c33f28b..b86f7ea1 100644 --- a/python/src/StrixelRemap/bind_StrixelPixelMapDefs.hpp +++ b/python/src/StrixelRemap/bind_StrixelPixelMapDefs.hpp @@ -156,7 +156,17 @@ void define_PixelStrixelMapDefs(py::module &m) { return py::array_t( self.map.shape(), self.map.data(), py::cast(&self, py::return_value_policy::reference)); - }) + }, + R"( + Strixel-to-pixel order map. + + Returns + ------- + numpy.ndarray[int] + Two-dimensional array of flattened pixel indices. + A value of ``-1`` indicates that no valid source pixel is + mapped at that position. + )") .def("empty", &aare::remap::defs::StrixelGroupToPixelMap::empty, R"( Check if the map is empty. diff --git a/python/src/StrixelRemap/bind_StrixelRemap.hpp b/python/src/StrixelRemap/bind_StrixelRemap.hpp index e7de46d4..cb6da9b3 100644 --- a/python/src/StrixelRemap/bind_StrixelRemap.hpp +++ b/python/src/StrixelRemap/bind_StrixelRemap.hpp @@ -36,7 +36,7 @@ void define_StrixelPixelRemaps(py::module &m) { Parameters ---------- - user_roi : InclusiveROI + user_roi : ROI User-specified ROI in the module's native coordinate system. )") @@ -89,12 +89,12 @@ void define_StrixelPixelRemaps(py::module &m) { ---------- user_roi : ROI User-specified ROI in the module's native coordinate system. - input : NDView[uint16_t, 2] + input : np.ndarray[uint16_t, 2] Input array to be remapped. Returns ------- - list of NDArray[uint16_t, 2] + list of np.ndarray[uint16_t, 2] Remapped arrays for each strixel group. If a group is not covered by the user ROI, the corresponding array will be None. )") @@ -129,15 +129,15 @@ void define_StrixelPixelRemaps(py::module &m) { Apply the strixel-to-pixel remapping to an input array. This overload assumes that the user ROI has already been set and the map calculated using `calculate_map()`. - + Parameters ---------- - input : NDView[uint16_t, 2] + input : np.ndarray[uint16_t, 2] Input array to be remapped. Returns ------- - list of NDArray[uint16_t, 2] + list of np.ndarray[uint16_t, 2] Remapped arrays for each strixel group. If a group is not covered by the user ROI, the corresponding array will be None. )") @@ -202,12 +202,12 @@ void define_StrixelPixelRemaps(py::module &m) { Apply the strixel-to-pixel remapping to an input array. This overload assumes that the user ROI has already been set and the map calculated using `calculate_map()`. - + Parameters ---------- - input : NDView[uint16_t, 2] + input : np.ndarray[uint16_t, 2] Input array to be remapped. - output : list of NDArray[uint16_t, 2] + output : list of np.ndarray[uint16_t, 2] Preallocated arrays to store the remapped results for each strixel group. If a group is not covered by the user ROI, the corresponding output array will be None. )");