diff --git a/newsfragments/743.misc b/newsfragments/743.misc new file mode 100644 index 000000000..5574bbb6f --- /dev/null +++ b/newsfragments/743.misc @@ -0,0 +1 @@ +Additional methods for FormatISISSXD to extract data more efficiently. diff --git a/src/dxtbx/format/FormatISISSXD.py b/src/dxtbx/format/FormatISISSXD.py index 00299d515..549fe3064 100644 --- a/src/dxtbx/format/FormatISISSXD.py +++ b/src/dxtbx/format/FormatISISSXD.py @@ -3,6 +3,7 @@ from typing import List, Tuple import h5py +import numpy as np import cctbx.array_family.flex as flex @@ -44,6 +45,9 @@ def get_name(image_file: str) -> str: return get_name(image_file) == "SXD" + def get_instrument_name(self) -> str: + return "SXD" + def get_experiment_title(self) -> str: return self._nxs_file["raw_data_1"]["title"][0].decode() @@ -55,23 +59,26 @@ def get_experiment_description(self) -> str: run_number = self.get_experiment_run_number() return f"{title} ({run_number})" - def get_goniometer(self, idx: int = None) -> Goniometer: - rotation_axis = (0.0, 1.0, 0.0) - fixed_rotation = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) - goniometer = GoniometerFactory.make_goniometer(rotation_axis, fixed_rotation) + def get_goniometer_phi_angle(self) -> float: try: experiment_title = self.get_experiment_title() if "w=" in experiment_title: - angle = float(experiment_title().split("w=")[1].split()[0]) + return float(experiment_title.split("w=")[1].split()[0]) elif "wccr" in experiment_title: - angle = float(experiment_title.split("wccr=")[1].split()[0]) + return float(experiment_title.split("wccr=")[1].split()[0]) elif "wtl" in experiment_title: - angle = float(experiment_title.split("wtl=")[1].split()[0]) + return float(experiment_title.split("wtl=")[1].split()[0]) else: - return goniometer - goniometer.rotate_around_origin(rotation_axis, angle) + return 0 except (ValueError, IndexError): - pass + return 0 + + def get_goniometer(self, idx: int = None) -> Goniometer: + rotation_axis = (0.0, 1.0, 0.0) + fixed_rotation = (1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0) + goniometer = GoniometerFactory.make_goniometer(rotation_axis, fixed_rotation) + angle = self.get_goniometer_phi_angle() + goniometer.rotate_around_origin(rotation_axis, angle) return goniometer def get_detector(self, index: int = None) -> Detector: @@ -313,3 +320,71 @@ def get_raw_data(self, index: int, use_loaded_data=True) -> Tuple[flex.int]: raw_data.append(panel_data) return tuple(raw_data) + + def get_flattened_data( + self, image_range: None | Tuple = None, scale_data: bool = True + ) -> Tuple[flex.int]: + """ + Image data summed along the time-of-flight direction + """ + + panel_size = self._get_image_size() + total_pixels = panel_size[0] * panel_size[1] + # Panel positions are offset by 4 in raw_data array + # See p24 of https://www.isis.stfc.ac.uk/Pages/sxd-user-guide6683.pdf + idx_offset = 4 + max_val = None + num_tof_bins = len(self._get_time_of_flight()) + raw_data = [] + for panel_idx in range(self._get_num_panels()): + start_idx = (panel_idx * total_pixels) + (panel_idx * idx_offset) + end_idx = start_idx + total_pixels + panel_data = self._nxs_file["raw_data_1/detector_1/counts"][ + 0, start_idx:end_idx, : + ] + panel_data = np.reshape( + panel_data, (panel_size[0], panel_size[1], num_tof_bins) + ) + if image_range is not None: + assert ( + len(image_range) == 2 + ), "expected image_range to be only two values" + assert ( + image_range[0] >= 0 and image_range[0] < image_range[1] + ), "image_range[0] out of range" + assert image_range[1] <= num_tof_bins, "image_range[1] out of range" + panel_data = np.flipud( + np.sum(panel_data[:, :, image_range[0] : image_range[1]], axis=2) + ) + else: + panel_data = np.flipud(np.sum(panel_data, axis=2)) + if panel_idx == 0 and self._panel_0_flipped(): + panel_data = np.flipud(panel_data) + panel_max_val = np.max(panel_data) + if max_val is None or max_val < panel_max_val: + max_val = panel_max_val + raw_data.append(panel_data) + + if scale_data: + return tuple([(i / max_val).tolist() for i in raw_data]) + + return tuple([i.tolist() for i in raw_data]) + + def get_flattened_pixel_data( + self, panel_idx: int, x: int, y: int + ) -> Tuple[Tuple, Tuple]: + time_channels = self._get_time_of_flight() + panel_size = self._get_image_size() + height = panel_size[1] + total_pixels = panel_size[0] * panel_size[1] + # Panel positions are offset by 4 in raw_data array + # See p24 of https://www.isis.stfc.ac.uk/Pages/sxd-user-guide6683.pdf + idx_offset = 4 + idx = (panel_idx * total_pixels) + (panel_idx * idx_offset) + y * height + x + return ( + time_channels, + tuple(self._nxs_file["raw_data_1/detector_1/counts"][0, idx, :].tolist()), + ) + + def get_proton_charge(self): + return float(self._nxs_file["raw_data_1/proton_charge"][0])