python bindings for strixel remap

This commit is contained in:
2026-09-07 22:53:58 +02:00
parent a3cce166f0
commit f8eeca2f7a
12 changed files with 841 additions and 3 deletions
+2
View File
@@ -46,6 +46,8 @@ set(PYTHON_FILES
aare/RawFile.py
aare/transform.py
aare/ScanParameters.py
aare/strixelremap/__init__.py
aare/strixelremap/SensorConfig.py
aare/utils.py)
# Copy the python files to the build directory
+2
View File
@@ -67,3 +67,5 @@ from ._aare import (
PixelHistogram_u32,
PixelHistogram_u64,
)
from . import strixelremap
+22
View File
@@ -0,0 +1,22 @@
from .._aare import strixelremap
def SensorConfig(sensor_geometry, group_configs):
"""
Helper function to create a sensor configuration from a sensor geometry and a list of group configurations
Args:
sensor_geometry (SensorPixelGeometry): The sensor geometry
group_configs (list[GroupConfig]): The list of group configurations
"""
if(len(group_configs) == 0):
raise ValueError("group_configs must contain at least one group configuration")
N = len(group_configs)
if N > 4:
raise ValueError("sensor configs with more than 4 groups are not bound in Python")
sensor_config_cls = getattr(strixelremap, f"SensorConfig_{N}PixelGroups")
return sensor_config_cls(sensor_geometry, group_configs)
+4
View File
@@ -0,0 +1,4 @@
from .SensorConfig import SensorConfig
from .._aare.strixelremap import *
@@ -0,0 +1,109 @@
#include <pybind11/pybind11.h>
#include "aare/InclusiveROI.hpp"
namespace py = pybind11;
// TODO: How to import like aare import StrixelPixelRemap
void define_InclusiveROI(py::module &m) {
py::class_<aare::InclusiveROI>(m, "InclusiveROI")
.def(py::init<int, int, int, int>(), py::arg("xmin"), py::arg("xmax"),
py::arg("ymin"), py::arg("ymax"))
.def_readwrite("xmin", &aare::InclusiveROI::xmin,
"minimum x coordinate (inclusive)")
.def_readwrite("xmax", &aare::InclusiveROI::xmax,
"maximum x coordinate (inclusive)")
.def_readwrite("ymin", &aare::InclusiveROI::ymin,
"minimum y coordinate (inclusive)")
.def_readwrite("ymax", &aare::InclusiveROI::ymax,
"maximum y coordinate (inclusive)")
.def_property_readonly("width", &aare::InclusiveROI::width,
"width of the ROI")
.def_property_readonly("height", &aare::InclusiveROI::height,
"height of the ROI")
.def_property_readonly("size", &aare::InclusiveROI::size,
"number of pixels in the ROI")
.def("is_empty", &aare::InclusiveROI::is_empty,
"check if the ROI is empty")
.def(
"contains",
[](const aare::InclusiveROI &self, int x, int y) {
return self.contains(x, y);
},
R"(
check if a point is contained in the ROI
Parameters
----------
x : int
x coordinate of the point
y : int
y coordinate of the point
Returns
-------
bool
True if the point is contained in the ROI, False otherwise
)")
.def(
"fits_in",
[](const aare::InclusiveROI &self, int ncols, int nrows) {
return self.fits_in(ncols, nrows);
},
R"(
check if the ROI fits within a given number of columns and rows
Parameters
----------
ncols : int
number of columns
nrows : int
number of rows
Returns
-------
bool
True if the ROI fits within the given dimensions, False otherwise
)")
.def("__eq__", &aare::InclusiveROI::operator==, py::is_operator(),
"check if two InclusiveROI objects are equal")
.def_static("emptyROI", &aare::InclusiveROI::emptyROI,
"create an empty InclusiveROI");
m.def("toInclusiveROI", &toInclusiveROI, py::arg("roi").noconvert(),
R"(
Convert a half-open ROI to an inclusive ROI
Parameters
----------
roi : ROI
Half-open ROI to be converted
Returns
-------
InclusiveROI
Inclusive ROI with the same physical extent
)");
m.def("toHalfopenROI", &toHalfopenROI, py::arg("roi").noconvert(),
R"(
Convert an inclusive ROI to a half-open ROI
Parameters
----------
roi : InclusiveROI
Inclusive ROI to be converted
Returns
-------
ROI
Half-open ROI with the same physical extent
)");
}
@@ -0,0 +1,330 @@
#include <pybind11/pybind11.h>
#include "aare/StrixelPixelRemapConfig.hpp"
#include "aare/StrixelPixelRemapGenerate.hpp"
namespace py = pybind11;
void define_predefinedConfigs(py::module &m) {
// Predefined strixel geometries
m.attr("StrxP25") = aare::remap::config::jungfrau::StrxP25;
// Strixel geometry for 25 µm pitch strixels on iLGAD sensors (multiplicity
// = 3)
m.attr("StrxP15") = aare::remap::config::jungfrau::StrxP15;
//"Strixel geometry for 15 µm pitch strixels on iLGAD sensors "
//"(multiplicity = 5)";
m.attr("StrxP18") = aare::remap::config::jungfrau::StrxP18;
//"Strixel geometry for 18 µm pitch strixels on iLGAD sensors "
//"(multiplicity = 4)";
m.attr("StrxP37") = aare::remap::config::jungfrau::StrxP37;
//"Strixel geometry for 37 µm pitch strixels on iLGAD sensors "
//"(multiplicity = 2)";
// Predefined sensor placements
m.attr("Chip1") = aare::remap::config::jungfrau::Chip1;
// Placement of the 2x2cm iLGAD sensor on the second chip (Chip1) of the
// Jungfrau module with no rotation applied.
m.attr("Chip6") = aare::remap::config::jungfrau::Chip6;
//"Placement of the 2x2cm iLGAD sensor on the seventh chip (Chip6) of "
//"the Jungfrau module"
//"with a 180-degree rotation applied.";
m.attr("Quad") = aare::remap::config::jungfrau::Quad;
//"Placement of the 4x4cm iLGAD sensor on the quad "
//"(Chip1+Chip2+Chip5+Chip6) of the Jungfrau module"
//"with no rotation applied.";
// Predefined sensor geometries
m.attr("SingleChipMP_iLGAD_pix") =
aare::remap::config::jungfrau::SingleChipMP_iLGAD_pix;
// "Pixel geometry of the 2x2 cm iLGAD sensor";
m.attr("Quad_iLGAD_pix") = aare::remap::config::jungfrau::Quad_iLGAD_pix;
// "Pixel geometry of the 4x4 cm iLGAD sensor";
m.attr("SingleChipMP_TEW_pix") =
aare::remap::config::jungfrau::SingleChipMP_TEW_pix;
// "Pixel geometry of the 2x2 cm TEW sensor";
// Predefined strixel groups
m.attr("SingleChipMP_iLGAD_P25") =
aare::remap::config::jungfrau::SingleChipMP_iLGAD_P25;
// "Strixel group of 25 µm pitch strixels on the 2x2 cm iLGAD sensor";
m.attr("SingleChipMP_iLGAD_P15") =
aare::remap::config::jungfrau::SingleChipMP_iLGAD_P15;
// "Strixel group of 15 µm pitch strixels on the 2x2 cm iLGAD sensor";
m.attr("SingleChipMP_iLGAD_P18") =
aare::remap::config::jungfrau::SingleChipMP_iLGAD_P18;
// "Strixel group of 18.75 µm pitch strixels on the 2x2 cm iLGAD sensor";
m.attr("SingleChipMP_TEW_P25") =
aare::remap::config::jungfrau::SingleChipMP_TEW_P25;
// "Strixel group of 25 µm pitch strixels on the 2x2 cm TEW sensor";
m.attr("SingleChipMP_TEW_P15") =
aare::remap::config::jungfrau::SingleChipMP_TEW_P15;
// "Strixel group of 15 µm pitch strixels on the 2x2 cm TEW sensor";
m.attr("SingleChipMP_TEW_P18") =
aare::remap::config::jungfrau::SingleChipMP_TEW_P18;
// "Strixel group of 18.75 µm pitch strixels on the 2x2 cm TEW sensor";
m.attr("Quad_iLGAD_bottomhalf") =
aare::remap::config::jungfrau::Quad_iLGAD_bottomhalf;
// "Strixel group of 25 µm pitch strixels located on the bottom half of "
// "4x4 cm iLGAD sensor";
m.attr("Quad_iLGAD_tophalf") =
aare::remap::config::jungfrau::Quad_iLGAD_tophalf;
// "Strixel group of 25 µm pitch strixels located on the top half of 4x4 "
// "cm iLGAD sensor";
// Predefined sensor configurations
m.attr("SingleChipMP_iLGAD") =
aare::remap::config::jungfrau::SingleChipMP_iLGAD;
// "Sensor configuration of the 2x2 cm iLGAD sensor with all strixel
// groups";
m.attr("SingleChipMP_TEW") =
aare::remap::config::jungfrau::SingleChipMP_TEW;
// "Sensor configuration of the 2x2 cm TEW sensor with all strixel groups";
m.attr("Quad_iLGAD") = aare::remap::config::jungfrau::Quad_iLGAD;
// "Sensor configuration of the 4x4 cm iLGAD sensor with all strixel "
// "groups";
}
void define_predefinedStrixelPixelMaps(py::module &m) {
m.attr("jungfrau_ilgad_singlechip_25um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_singlechip_25um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the Strx25 strixel group on a Jungfrau ILGAD sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_ilgad_singlechip_15um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_singlechip_15um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the Strx15 strixel group on a Jungfrau ILGAD sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_ilgad_singlechip_18um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_singlechip_18um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the Strx18 strixel group on a Jungfrau ILGAD sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_ilgad_strixel_maps") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_quad_25um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a list of strixel-to-pixel remapping map for each strixel group on a Jungfrau ILGAD quad sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
list[StrixelGroupToPixelMap]
A list of StrixelGroupToPixelMap, one for each strixel group on the sensor.
Each map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
// TEW sensor strixel maps
m.attr("jungfrau_tew_singlechip_25um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_tew_singlechip_25um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the Strx25 strixel group on a Jungfrau TEW sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_tew_singlechip_15um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_tew_singlechip_15um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the Strx15 strixel group on a Jungfrau TEW sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_tew_singlechip_18um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_tew_singlechip_18um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the Strx18 strixel group on a Jungfrau TEW sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_tew_strixel_maps") = py::cpp_function(
&aare::remap::generate::jungfrau_tew_singlechip_multipitch_strixel_maps,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a list of strixel-to-pixel remapping map for each strixel group on a Jungfrau TEW sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
list[StrixelGroupToPixelMap]
A list of StrixelGroupToPixelMap, one for each strixel group on the sensor.
Each map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
// Jungfrau quad sensor strixel maps
m.attr("jungfrau_ilgad_quadbottom_25um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_quadbottom_25um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the bottom half of the Strx25 strixel group on a Jungfrau ILGAD quad sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_ilgad_quadtop_25um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_quadtop_25um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the top half of the Strx25 strixel group on a Jungfrau ILGAD quad sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
m.attr("jungfrau_ilgad_quad_25um_strixel_map") = py::cpp_function(
&aare::remap::generate::jungfrau_ilgad_quad_25um_strixel_map,
py::arg("user_roi").noconvert(), py::arg("placement").noconvert(),
py::arg("bond_shift").noconvert() = aare::remap::defs::BondShift{0, 0},
R"(
Generates a strixel-to-pixel remapping map for the entire Strx25 strixel group on a Jungfrau ILGAD quad sensor
Parameters
----------
user_roi : InclusiveROI
ROI in global module coordinate system.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
bond_shift : BondShift, optional
Bonding shift applied before the configured sensor placement rotation. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
combined maps of the bottom and top halves of the Strx25 strixel group on a Jungfrau ILGAD quad sensor.
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
}
@@ -0,0 +1,259 @@
#include <pybind11/pybind11.h>
#include "aare/StrixelPixelRemapDefs.hpp"
namespace py = pybind11;
// TODO: How to import like aare import StrixelPixelRemap
void define_PixelStrixelMapDefs(py::module &m) {
py::enum_<aare::remap::defs::Rotation>(m, "Rotation")
.value("Identity", aare::remap::defs::Rotation::Identity)
.value("Rotate180", aare::remap::defs::Rotation::Rotate180)
.export_values();
py::enum_<aare::remap::defs::ModuloOrdering>(m, "ModuloOrdering")
.value("Forward", aare::remap::defs::ModuloOrdering::Forward)
.value("Reverse", aare::remap::defs::ModuloOrdering::Reverse)
.export_values();
py::class_<aare::remap::defs::Guardring>(m, "Guardring")
.def(py::init<int, int>(), py::arg("x"), py::arg("y"))
.def_readwrite("x", &aare::remap::defs::Guardring::x,
"ring width in pixels")
.def_readwrite("y", &aare::remap::defs::Guardring::y,
"ring height in pixels")
.def(
"__eq__",
[](const aare::remap::defs::Guardring &self,
const aare::remap::defs::Guardring &other) {
return self.x == other.x && self.y == other.y;
},
py::is_operator())
.def("__repr__", [](const aare::remap::defs::Guardring &self) {
return fmt::format("Guardring(x={}, y={})", self.x, self.y);
});
py::class_<aare::remap::defs::BondShift>(m, "BondShift")
.def(py::init<int, int>(), py::arg("x"), py::arg("y"))
.def_readwrite("x", &aare::remap::defs::BondShift::x,
"bond shift in x direction (pixels)")
.def_readwrite("y", &aare::remap::defs::BondShift::y,
"bond shift in y direction (pixels)");
py::class_<aare::remap::defs::SensorPixelGeometry>(m, "SensorPixelGeometry")
.def(py::init<int, int, aare::remap::defs::Guardring>(),
py::arg("num_pix_x"), py::arg("num_pix_y"),
py::arg("guardring") = aare::remap::defs::Guardring{0, 0})
.def_readwrite("num_pix_x",
&aare::remap::defs::SensorPixelGeometry::num_pix_x,
"number of pixels in x direction")
.def_readwrite("num_pix_y",
&aare::remap::defs::SensorPixelGeometry::num_pix_y,
"number of pixels in y direction")
.def_readwrite(
"guardring", &aare::remap::defs::SensorPixelGeometry::guardring,
"physical guardring around the sensor (default Guardring(0,0))");
py::class_<aare::remap::defs::GroupStrixelGeometry>(m,
"GroupStrixelGeometry")
.def(py::init<int, double>(), py::arg("multiplicity"),
py::arg("pitch_um"))
.def_readwrite("multiplicity",
&aare::remap::defs::GroupStrixelGeometry::multiplicity,
"maximum number of pixels a strixel covers")
.def_readwrite("pitch_um",
&aare::remap::defs::GroupStrixelGeometry::pitch_um,
"effective minimal strixel pitch [µm]");
py::class_<aare::remap::defs::GroupRouting>(m, "GroupRouting")
.def(py::init<aare::remap::defs::ModuloOrdering>(),
py::arg("mod_order") = aare::remap::defs::ModuloOrdering::Forward)
.def_readwrite(
"mod_order", &aare::remap::defs::GroupRouting::mod_order,
"modulo ordering of pixels within each strixel multiplicity group "
"default(ModuloOrdering::Forward)");
py::class_<aare::remap::defs::GroupConfig>(m, "GroupConfig")
.def(py::init<aare::remap::defs::GroupStrixelGeometry,
aare::remap::defs::GroupRouting, aare::InclusiveROI>(),
py::arg("strixel"), py::arg("routing"),
py::arg("placement_on_sensor"))
.def(py::init([](const aare::remap::defs::GroupStrixelGeometry &strixel,
const aare::remap::defs::ModuloOrdering &mod_order,
const aare::InclusiveROI &placement_on_sensor) {
return aare::remap::defs::GroupConfig{
strixel, {mod_order}, placement_on_sensor};
}),
py::arg("strixel"), py::arg("routing"),
py::arg("placement_on_sensor"))
.def_readwrite("strixel", &aare::remap::defs::GroupConfig::strixel,
"strixel geometry of the group")
.def_readwrite("routing", &aare::remap::defs::GroupConfig::routing,
"pixel-to-strixel routing pattern")
.def_readwrite(
"placement_on_sensor",
&aare::remap::defs::GroupConfig::placement_on_sensor,
"placement of the strixel group on the sensor (sensor-local "
"coordinates)");
py::class_<aare::remap::defs::SensorModulePlacement>(
m, "SensorModulePlacement")
.def(py::init<aare::InclusiveROI, aare::remap::defs::Rotation>(),
py::arg("placement_on_module"), py::arg("rotation"))
.def_readwrite(
"placement_on_module",
&aare::remap::defs::SensorModulePlacement::placement_on_module,
"sensor bounds in module coordinates")
.def_readwrite(
"rotation", &aare::remap::defs::SensorModulePlacement::rotation,
"physical orientation of the mounted sensor-ASIC assembly with "
"respect to the module reference frame");
py::class_<aare::remap::defs::StrixelGroupToPixelMap>(
m, "StrixelGroupToPixelMap")
.def(py::init<>())
.def_readonly("effective_roi",
&aare::remap::defs::StrixelGroupToPixelMap::effective_roi,
"effective pixel ROI covered by this map (InclusiveROI)")
.def_property_readonly(
"map",
[](const aare::remap::defs::StrixelGroupToPixelMap &self)
-> py::array {
return py::array_t<ssize_t>(
self.map.shape(), self.map.data(),
py::cast(&self, py::return_value_policy::reference));
});
}
template <std::size_t N> void define_SensorConfig(py::module &m) {
const auto class_name = fmt::format("SensorConfig_{}PixelGroups", N);
py::class_<aare::remap::defs::SensorConfig<N>>(m, class_name.c_str())
.def(py::init<aare::remap::defs::SensorPixelGeometry,
std::array<aare::remap::defs::GroupConfig, N>>(),
py::arg("pixel"), py::arg("group_configs"))
.def_readwrite("pixel", &aare::remap::defs::SensorConfig<N>::pixel,
"sensor pixel geometry (SensorPixelGeometry)")
.def_readwrite("group_configs",
&aare::remap::defs::SensorConfig<N>::group_configs,
"[list] of strixel group configurations");
}
void define_RemapAlgorithm(py::module &m) {
m.def("strixel_to_pixel_map", &aare::remap::algo::strixel_to_pixel_map,
py::arg("group_config").noconvert(), py::arg("pixel").noconvert(),
py::arg("placement").noconvert(), py::arg("user_roi").noconvert(),
py::arg("bond_shift").noconvert() =
aare::remap::defs::BondShift{0, 0},
R"(
Creates a StrixeltoPixelMap for a specific GroupConfig
Parameters
----------
group_config : GroupConfig
Configuration of the strixel group.
pixel : SensorPixelGeometry
Pixel geometry of the sensor.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
user_roi : InclusiveROI
User-defined region of interest. (in global module coordinates)
bond_shift : BondShift, optional
Shift applied to the bond positions. Default is (0, 0).
Returns
-------
StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
// cant use np.take or have to mask -1 indices
m.def(
"ApplyRemap",
[](py::array input,
py::array_t<ssize_t, py::array::c_style | py::array::forcecast>
order_map,
py::array output) {
if (!(input.flags() & py::array::c_style) ||
!(output.flags() & py::array::c_style)) {
throw std::runtime_error("Arrays must be C-contiguous");
}
if (input.ndim() != 2 || output.ndim() != 2) {
throw std::runtime_error("Input and output arrays must be 2D");
}
auto i_dtype = input.dtype();
auto o_dtype = output.dtype();
if (!input.dtype().is(output.dtype())) {
throw std::runtime_error(
"Input and output arrays must have the same dtype");
}
auto input_array =
py::array_t<uint16_t, py::array::c_style |
py::array::forcecast>::ensure(input);
auto output_array =
py::array_t<uint16_t, py::array::c_style |
py::array::forcecast>::ensure(output);
aare::remap::algo::ApplyRemap(
make_view_2d(input_array), // TODO expecting uint16_t for now
make_view_2d(order_map), make_view_2d(output_array));
},
py::arg("input").noconvert(), py::arg("order_map").noconvert(),
py::arg("output").noconvert(),
R"(
Applies a given remapping rule to an input array.
Parameters
----------
input : np.array
Original array
order_map : np.array[ssize_t, 2]
Rule for remapping
output : np.array
Remapped array
)");
}
template <std::size_t N>
void define_RemapAlgorithmforSensorConfig(py::module &m) {
m.def("strixel_to_pixel_maps", &aare::remap::algo::strixel_to_pixel_maps<N>,
py::arg("sensor_config").noconvert(),
py::arg("placement").noconvert(), py::arg("user_roi").noconvert(),
py::arg("bond_shift").noconvert() =
aare::remap::defs::BondShift{0, 0},
R"(
Creates a StrixeltoPixelMap for all GroupConfigs in a SensorConfig
Parameters
----------
sensor_config : SensorConfig
Configuration of the sensor, including all configurations of the strixel groups.
placement : SensorModulePlacement
Placement and orientation of the sensor on the module.
user_roi : InclusiveROI
User-defined region of interest. (in global module coordinates)
bond_shift : BondShift, optional
Shift applied to the bond positions. Default is (0, 0).
Returns
-------
list of StrixelGroupToPixelMap
map(row, col) contains the flattened pixel index of the corresponding source pixel in the user-provided input ROI for strixel defined at (row, col).
An entry of -1 indicates that the corresponding strixel position has no valid source pixel.
)");
}
+21
View File
@@ -22,6 +22,10 @@
#include "bind_RawFile.hpp"
#include "bind_calibration.hpp"
#include "StrixelRemap/bind_InclusiveROI.hpp"
#include "StrixelRemap/bind_PredefinedVariables.hpp"
#include "StrixelRemap/bind_StrixelRemap.hpp"
// TODO! migrate the other names
#include "ctb_raw_file.hpp"
#include "file.hpp"
@@ -63,6 +67,10 @@ double, 'f' for float)
define_ClusterFileSink<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE); \
define_ClusterCollector<T, N, M, U>(m, "Cluster" #N "x" #M #TYPE_CODE);
#define DEFINE_BINDINGS_SENSORCONFIG(N) \
define_SensorConfig<N>(strixelremap); \
define_RemapAlgorithmforSensorConfig<N>(strixelremap);
PYBIND11_MODULE(_aare, m) {
auto experimental = m.def_submodule(
"experimental", "Experimental APIs that may change without notice");
@@ -182,4 +190,17 @@ PYBIND11_MODULE(_aare, m) {
define_eta<double>(m, "d");
define_eta<int>(m, "i");
define_eta<int16_t>(m, "i16");
auto strixelremap =
m.def_submodule("strixelremap", "Strixel remapping utilities.");
define_InclusiveROI(strixelremap);
define_PixelStrixelMapDefs(strixelremap);
define_RemapAlgorithm(strixelremap);
DEFINE_BINDINGS_SENSORCONFIG(1);
DEFINE_BINDINGS_SENSORCONFIG(2);
DEFINE_BINDINGS_SENSORCONFIG(4);
DEFINE_BINDINGS_SENSORCONFIG(3);
define_predefinedConfigs(strixelremap);
define_predefinedStrixelPixelMaps(strixelremap);
}
+88
View File
@@ -0,0 +1,88 @@
import pytest
import numpy as np
from aare import strixelremap
from aare import ROI
def test_roimappings():
""" Test exclusive to inclusive roi mapping and vice versa """
roi = ROI(0, 10, 0, 5)
inclusive_roi = strixelremap.toInclusiveROI(roi)
assert inclusive_roi.xmin == 0
assert inclusive_roi.xmax == 9
assert inclusive_roi.ymin == 0
assert inclusive_roi.ymax == 4
exclusive_roi = strixelremap.toHalfopenROI(inclusive_roi)
assert exclusive_roi.xmin == 0
assert exclusive_roi.xmax == 10
assert exclusive_roi.ymin == 0
assert exclusive_roi.ymax == 5
def test_emptyROI():
""" Test creation of an empty ROI """
empty_roi = strixelremap.InclusiveROI.emptyROI()
assert empty_roi.xmin == 0
assert empty_roi.xmax == -1
assert empty_roi.ymin == 0
assert empty_roi.ymax == -1
def test_customSensorConfiguration():
""" Test that a custom sensor configuration can be created and used to remap strixel pixels """
my_strixel_group = strixelremap.GroupStrixelGeometry(multiplicity=2, pitch_um=25.0)
assert my_strixel_group.multiplicity == 2
assert my_strixel_group.pitch_um == 25.0
my_sensor_geometry = strixelremap.SensorPixelGeometry(num_pix_x = 10, num_pix_y = 5)
assert my_sensor_geometry.num_pix_x == 10
assert my_sensor_geometry.num_pix_y == 5
assert my_sensor_geometry.guardring == strixelremap.Guardring(0,0)
my_group_config = strixelremap.GroupConfig(strixel = my_strixel_group, routing = strixelremap.ModuloOrdering.Forward,placement_on_sensor = strixelremap.InclusiveROI(0,9,0,4))
my_sensor_config = strixelremap.SensorConfig(sensor_geometry = my_sensor_geometry, group_configs = [my_group_config])
# rebase
user_roi = strixelremap.InclusiveROI(strixelremap.Chip1.placement_on_module.xmin + 0, strixelremap.Chip1.placement_on_module.xmin + 9, strixelremap.Chip1.placement_on_module.ymin + 0, strixelremap.Chip1.placement_on_module.ymin + 4)
strixelpixelmap = strixelremap.strixel_to_pixel_maps(sensor_config = my_sensor_config, placement = strixelremap.Chip1, user_roi = user_roi)
assert len(strixelpixelmap) == 1
assert strixelpixelmap[0].effective_roi == my_group_config.placement_on_sensor
map = strixelpixelmap[0].map
assert map.shape == (10 ,5)
assert np.array_equal(map, np.array([[0, 2, 4, 6, 8], [1, 3, 5, 7, 9], [10, 12, 14, 16, 18], [11, 13, 15, 17, 19], [20, 22, 24, 26, 28], [21, 23, 25, 27, 29], [30, 32, 34, 36, 38], [31, 33, 35, 37, 39], [40, 42, 44, 46, 48], [41, 43, 45, 47, 49]]))
def test_predefinedRemap():
""" Test predefined maps API """
user_roi = strixelremap.InclusiveROI(strixelremap.Chip1.placement_on_module.xmin + 5, strixelremap.Chip1.placement_on_module.xmin + 9, strixelremap.Chip1.placement_on_module.ymin + 5, strixelremap.Chip1.placement_on_module.ymin + 7)
strixelpixelmap = strixelremap.jungfrau_tew_singlechip_25um_strixel_map(user_roi = user_roi, placement = strixelremap.Chip1)
print(strixelpixelmap.map)
assert strixelpixelmap.map.shape == (9, 2)
assert np.array_equal(strixelpixelmap.map, np.array([[-1, 2], [0, 3], [1, 4], [-1, 7], [5,8], [6,9], [-1,12], [10,13], [11,14]]))
# test apply remap
# Documenation, Example