diff --git a/newsfragments/XXX.misc b/newsfragments/XXX.misc new file mode 100644 index 000000000..42225ae11 --- /dev/null +++ b/newsfragments/XXX.misc @@ -0,0 +1 @@ +Add overloads for panel resolution methods to use beam objects instead of s0 directly. diff --git a/src/dxtbx/dxtbx_model_ext.pyi b/src/dxtbx/dxtbx_model_ext.pyi index fac8393b7..1de0e1da6 100644 --- a/src/dxtbx/dxtbx_model_ext.pyi +++ b/src/dxtbx/dxtbx_model_ext.pyi @@ -637,15 +637,32 @@ class Panel(PanelData): def get_gain(self) -> float: ... def get_identifier(self) -> str: ... def get_image_size_mm(self) -> Vec2Float: ... + @overload def get_max_resolution_at_corners(self, s0: Vec3Float) -> float: ... + @overload + def get_max_resolution_at_corners(self, beam: Beam) -> float: ... + @overload + def get_max_resolution_at_corners(self, beam: PolychromaticBeam) -> float: ... + @overload def get_max_resolution_ellipse(self, s0: Vec3Float) -> float: ... + @overload + def get_max_resolution_ellipse(self, beam: Beam) -> float: ... + @overload + def get_max_resolution_ellipse(self, beam: PolychromaticBeam) -> float: ... def get_normal_origin_px(self) -> Vec2Float: ... def get_pedestal(self) -> float: ... def get_pixel_lab_coord(self, px: Vec2Float) -> Vec3Float: ... def get_projection_2d(self) -> Union[Tuple[Vec4Int, Vec2Int], Tuple[()]]: ... def get_px_mm_strategy(self) -> PxMmStrategy: ... def get_ray_intersection_px(self, s1: Vec3Float) -> Vec2Float: ... + @overload def get_resolution_at_pixel(self, s0: Vec3Float, xy: Vec2Float) -> float: ... + @overload + def get_resolution_at_pixel(self, beam: Beam, xy: Vec2Float) -> float: ... + @overload + def get_resolution_at_pixel( + self, beam: PolychromaticBeam, xy: Vec2Float + ) -> float: ... def get_trusted_range_mask( self, image: Union[flex.int, flex.double] ) -> flex.bool: ... diff --git a/src/dxtbx/model/boost_python/panel.cc b/src/dxtbx/model/boost_python/panel.cc index 9006d801e..2e0b85ec9 100644 --- a/src/dxtbx/model/boost_python/panel.cc +++ b/src/dxtbx/model/boost_python/panel.cc @@ -349,6 +349,28 @@ namespace dxtbx { namespace model { namespace boost_python { return result; } + // Overloads for resolution methods + double (Panel::*get_resolution_at_pixel_s0)(vec3, vec2) const = + &Panel::get_resolution_at_pixel; + double (Panel::*get_resolution_at_pixel_beam)(Beam, vec2) const = + &Panel::get_resolution_at_pixel; + double (Panel::*get_resolution_at_pixel_pbeam)(PolychromaticBeam, vec2) + const = &Panel::get_resolution_at_pixel; + + double (Panel::*get_max_resolution_at_corners_s0)(vec3) const = + &Panel::get_max_resolution_at_corners; + double (Panel::*get_max_resolution_at_corners_beam)(Beam) const = + &Panel::get_max_resolution_at_corners; + double (Panel::*get_max_resolution_at_corners_pbeam)(PolychromaticBeam) const = + &Panel::get_max_resolution_at_corners; + + double (Panel::*get_max_resolution_ellipse_s0)(vec3) const = + &Panel::get_max_resolution_ellipse; + double (Panel::*get_max_resolution_ellipse_beam)(Beam) const = + &Panel::get_max_resolution_ellipse; + double (Panel::*get_max_resolution_ellipse_pbeam)(PolychromaticBeam) const = + &Panel::get_max_resolution_ellipse; + void export_panel() { using namespace panel_detail; using namespace boost::python; @@ -530,9 +552,15 @@ namespace dxtbx { namespace model { namespace boost_python { .def("get_two_theta_at_pixel", &Panel::get_two_theta_at_pixel) .def("get_two_theta_array", &Panel::get_two_theta_array) .def("get_cos2_two_theta_array", &Panel::get_cos2_two_theta_array) - .def("get_resolution_at_pixel", &Panel::get_resolution_at_pixel) - .def("get_max_resolution_at_corners", &Panel::get_max_resolution_at_corners) - .def("get_max_resolution_ellipse", &Panel::get_max_resolution_ellipse) + .def("get_resolution_at_pixel", get_resolution_at_pixel_s0) + .def("get_resolution_at_pixel", get_resolution_at_pixel_beam) + .def("get_resolution_at_pixel", get_resolution_at_pixel_pbeam) + .def("get_max_resolution_at_corners", get_max_resolution_at_corners_s0) + .def("get_max_resolution_at_corners", get_max_resolution_at_corners_beam) + .def("get_max_resolution_at_corners", get_max_resolution_at_corners_pbeam) + .def("get_max_resolution_ellipse", get_max_resolution_ellipse_s0) + .def("get_max_resolution_ellipse", get_max_resolution_ellipse_beam) + .def("get_max_resolution_ellipse", get_max_resolution_ellipse_pbeam) .def("__deepcopy__", &panel_deepcopy) .def("__copy__", &panel_deepcopy) .def("__str__", &panel_to_string) diff --git a/src/dxtbx/model/panel.h b/src/dxtbx/model/panel.h index 59a92a3c0..15092b8aa 100644 --- a/src/dxtbx/model/panel.h +++ b/src/dxtbx/model/panel.h @@ -307,10 +307,31 @@ namespace dxtbx { namespace model { return 1.0 / (2.0 * s0.length() * sintheta); } + /** + * Get the resolution at a given pixel. + * @param xy The pixel coordinate + * @returns The resolution at that point. + */ + double get_resolution_at_pixel(Beam beam, vec2 xy) const { + vec3 s0 = beam.get_s0(); + return get_resolution_at_pixel(s0, xy); + } + + /** + * Get the resolution at a given pixel. + * @param xy The pixel coordinate + * @returns The resolution at that point. + */ + double get_resolution_at_pixel(PolychromaticBeam beam, vec2 xy) const { + vec3 unit_s0 = beam.get_unit_s0(); + double min_wavelength = beam.get_wavelength_range()[0]; + vec3 s0 = unit_s0 / min_wavelength; + return get_resolution_at_pixel(s0, xy); + } + /** * Get the maximum resolution of the detector (i.e. look at each corner * and find the maximum resolution.) - * @param beam The beam parameters * @returns The maximum resolution at the detector corners. */ double get_max_resolution_at_corners(vec3 s0) const { @@ -322,12 +343,33 @@ namespace dxtbx { namespace model { get_resolution_at_pixel(s0, vec2(fast, slow)))); } + /** + * Get the maximum resolution of the detector (i.e. look at each corner + * and find the maximum resolution.) + * @returns The maximum resolution at the detector corners. + */ + double get_max_resolution_at_corners(Beam beam) const { + vec3 s0 = beam.get_s0(); + return get_max_resolution_at_corners(s0); + } + + /** + * Get the maximum resolution of the detector (i.e. look at each corner + * and find the maximum resolution.) + * @returns The maximum resolution at the detector corners. + */ + double get_max_resolution_at_corners(PolychromaticBeam beam) const { + vec3 unit_s0 = beam.get_unit_s0(); + double min_wavelength = beam.get_wavelength_range()[0]; + vec3 s0 = unit_s0 / min_wavelength; + return get_max_resolution_at_corners(s0); + } + /** * Get the maximum resolution of a full circle on the detector. Get the * beam centre in pixels. Then find the coordinates on the edges making * a cross-hair with the beam centre. Calculate the resolution at these * corners and choose the minimum angle. - * @param beam The beam parameters * @returns The maximum resolution at the detector corners. */ double get_max_resolution_ellipse(vec3 s0) const { @@ -340,6 +382,32 @@ namespace dxtbx { namespace model { get_resolution_at_pixel(s0, vec2(c[0], slow)))); } + /** + * Get the maximum resolution of a full circle on the detector. Get the + * beam centre in pixels. Then find the coordinates on the edges making + * a cross-hair with the beam centre. Calculate the resolution at these + * corners and choose the minimum angle. + * @returns The maximum resolution at the detector corners. + */ + double get_max_resolution_ellipse(Beam beam) const { + vec3 s0 = beam.get_s0(); + return get_max_resolution_ellipse(s0); + } + + /** + * Get the maximum resolution of a full circle on the detector. Get the + * beam centre in pixels. Then find the coordinates on the edges making + * a cross-hair with the beam centre. Calculate the resolution at these + * corners and choose the minimum angle. + * @returns The maximum resolution at the detector corners. + */ + double get_max_resolution_ellipse(PolychromaticBeam beam) const { + vec3 unit_s0 = beam.get_unit_s0(); + double min_wavelength = beam.get_wavelength_range()[0]; + vec3 s0 = unit_s0 / min_wavelength; + return get_max_resolution_ellipse(s0); + } + /** Rotate the panel about an axis */ void rotate_around_origin(vec3 axis, double angle) { vec3 f = get_fast_axis().rotate_around_origin(axis, angle); diff --git a/src/dxtbx/model/virtual_panel.h b/src/dxtbx/model/virtual_panel.h index 3234730b0..853f54cbc 100644 --- a/src/dxtbx/model/virtual_panel.h +++ b/src/dxtbx/model/virtual_panel.h @@ -25,9 +25,12 @@ #include #include #include +#include namespace dxtbx { namespace model { + using dxtbx::model::Beam; + using dxtbx::model::PolychromaticBeam; using scitbx::mat3; using scitbx::vec2; using scitbx::vec3; diff --git a/tests/model/test_detector.py b/tests/model/test_detector.py index e7cd24418..ed6cc49db 100644 --- a/tests/model/test_detector.py +++ b/tests/model/test_detector.py @@ -11,7 +11,13 @@ from scitbx import matrix from scitbx.array_family import flex -from dxtbx.model import Beam, Detector, Panel, ParallaxCorrectedPxMmStrategy +from dxtbx.model import ( + Beam, + BeamFactory, + Detector, + Panel, + ParallaxCorrectedPxMmStrategy, +) from dxtbx.model.detector_helpers import ( get_detector_projection_2d_axes, get_panel_projection_2d_from_axes, @@ -452,3 +458,26 @@ def test_pickle_suite(): detector2 = pickle.loads(pickle.dumps(detector)) assert detector == detector2 + + +def test_detector_resolution(): + detector = create_detector(0) + beam = BeamFactory.make_beam((0, 0, -1), wavelength=0.8) + pbeam = BeamFactory.make_polychromatic_beam((0, 0, -1), wavelength_range=(0.8, 2.0)) + dmin1 = detector[0].get_resolution_at_pixel(beam.get_s0(), (1, 1)) + dmin2 = detector[0].get_resolution_at_pixel(beam, (1, 1)) + dmin3 = detector[0].get_resolution_at_pixel(pbeam, (1, 1)) + assert dmin1 == pytest.approx(dmin2) + assert dmin1 == pytest.approx(dmin3) + + dmin1 = detector[0].get_max_resolution_at_corners(beam.get_s0()) + dmin2 = detector[0].get_max_resolution_at_corners(beam) + dmin3 = detector[0].get_max_resolution_at_corners(pbeam) + assert dmin1 == pytest.approx(dmin2) + assert dmin1 == pytest.approx(dmin3) + + dmin1 = detector[0].get_max_resolution_ellipse(beam.get_s0()) + dmin2 = detector[0].get_max_resolution_ellipse(beam) + dmin3 = detector[0].get_max_resolution_ellipse(pbeam) + assert dmin1 == pytest.approx(dmin2) + assert dmin1 == pytest.approx(dmin3)