def test_px_mm_strategy_equality(): simple = SimplePxMmStrategy() assert simple == SimplePxMmStrategy() parallax_corrected = ParallaxCorrectedPxMmStrategy(1, 1) assert parallax_corrected == ParallaxCorrectedPxMmStrategy(1, 1) assert parallax_corrected != ParallaxCorrectedPxMmStrategy(1, 2) assert parallax_corrected != ParallaxCorrectedPxMmStrategy(2, 1) assert parallax_corrected != simple dx = flex.double(flex.grid(10, 10), 1) dy = flex.double(flex.grid(10, 10), 1) offset = OffsetPxMmStrategy(dx, dy) assert offset == OffsetPxMmStrategy(dx, dy) assert offset != simple assert offset != parallax_corrected offset_parallax_corrected = OffsetParallaxCorrectedPxMmStrategy( 1, 1, dx, dy) assert offset_parallax_corrected == OffsetParallaxCorrectedPxMmStrategy( 1, 1, dx, dy) assert offset_parallax_corrected != OffsetParallaxCorrectedPxMmStrategy( 1, 2, dx, dy) assert offset_parallax_corrected != OffsetParallaxCorrectedPxMmStrategy( 2, 1, dx, dy) assert offset_parallax_corrected != simple assert offset_parallax_corrected != parallax_corrected assert offset_parallax_corrected != offset
def test_panel_equality(): panel = create_detector(offset=0)[0] panel2 = copy.deepcopy(panel) assert panel == panel2 panel2.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(1, 1)) assert panel != panel2 panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(1, 1)) assert panel == panel2
def test_correction_on_random_coordinates(model): convert = ParallaxCorrectedPxMmStrategy(model["mu"], model["t0"]) for i in range(10000): xy = matrix.col((random.uniform(-1000, 1000), random.uniform(-1000, 1000))) # Do the forward and reverse corrections xy_corr_gold = matrix.col(correct_gold(model, xy)) xy_corr = matrix.col(convert.to_pixel(model["detector"], xy)) xy_corr_inv = matrix.col(convert.to_millimeter(model["detector"], xy_corr)) # Check the values assert abs(xy_corr_gold - xy_corr) < 1e-7 assert abs(xy_corr_inv - xy) < 1e-3
def _detector(self): """Partly dummy detector""" from scitbx import matrix # Get the detector geometry entry = self._h5_handle["entry"] instrument = entry["instrument"] detector = instrument["detector"] # Initialise detector frame - origin at 0,0,0 fast = matrix.col((1.0, 0.0, 0.0)) slow = matrix.col((0.0, 1.0, 0.0)) orig = matrix.col((0.0, 0.0, 0.0)) # Get the pixel and image size pixel_size = ( 1.0e-3 * detector["x_pixel_size"].value, 1.0e-3 * detector["y_pixel_size"].value, ) layout = detector["layout"].value[0].split("X") image_size = int(layout[0]), int(layout[1]) trusted_range = (-1, detector["saturation_value"][0]) thickness = float(detector["sensor_thickness"].value) / 1000.0 material = str(detector["sensor_material"].value[0]) # Make the detector detector = self._detector_factory.make_detector( "PAD", fast, slow, orig, pixel_size, image_size, trusted_range, name="Panel", thickness=thickness, material=material, ) # At the moment, beam is a dummy object because wavelength is not set in # the header. Therefore, the px<-->mm strategy will generally be # incorrect. Set it anyway, to override later. beam = self._beam() wavelength = beam.get_wavelength() from cctbx.eltbx import attenuation_coefficient from dxtbx.model import ParallaxCorrectedPxMmStrategy # this will fail for undefined composite materials table = attenuation_coefficient.get_table(material) # mu_at_angstrom returns cm^-1, but need mu in mm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 for panel in detector: panel.set_mu(mu) panel.set_px_mm_strategy( ParallaxCorrectedPxMmStrategy(mu, thickness)) return detector
def _detector(self): """Return a working detector instance, with added mask regions.""" detector = self._detector_factory.imgCIF_H(self._get_cbf_handle(), "PAD") for f0, f1, s0, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) m = re.search( r"^#\s*(\S+)\ssensor, thickness\s*([0-9.]+)\s*m\s*$", self._cif_header, re.MULTILINE, ) if m: # header gives thickness in metres, we store mm thickness = float(m.group(2)) * 1000 material = m.group(1) if material == "Silicon": material = "Si" for panel in detector: panel.set_thickness(thickness) panel.set_material(material) try: # a header only CBF file will not have a beam object beam = self._beam() except Exception: beam = None if beam: # attenuation coefficient depends on the beam wavelength wavelength = beam.get_wavelength() # this will fail for undefined composite materials table = attenuation_coefficient.get_table(material) # mu_at_angstrom returns cm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 for panel in detector: panel.set_px_mm_strategy( ParallaxCorrectedPxMmStrategy(mu, thickness) ) panel.set_mu(mu) m = re.search(r"^#\s*Detector:\s+(.*?)\s*$", self._cif_header, re.MULTILINE) if m and m.group(1): panel.set_identifier(m.group(1).encode()) size = detector[0].get_image_size() if size == (2463, 2527): self.vendortype = "Pilatus-6M" elif size == (1475, 1679): self.vendortype = "Pilatus-2M" elif size == (487, 619): self.vendortype = "Pilatus-300K" return detector
def get_values(invert_y): from dxtbx.model.beam import beam_factory beam = beam_factory.simple(wavelength=1) if invert_y: y_direction = "-y" else: y_direction = "+y" from dxtbx.model.detector import detector_factory detector = detector_factory.simple( sensor=detector_factory.sensor("PAD"), distance=100, beam_centre=[50, 50], fast_direction="+x", slow_direction=y_direction, pixel_size=[0.1, 0.1], image_size=[1000, 1000]) from dxtbx.model import ParallaxCorrectedPxMmStrategy from cctbx.eltbx import attenuation_coefficient wavelength = beam.get_wavelength() thickness = 0.5 table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness for panel in detector: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) v1 = detector[0].pixel_to_millimeter((0, 0)) v2 = detector[0].pixel_to_millimeter((1000, 1000)) return v1, v2
def test_parallax_correction(): # Attenuation length table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(1) / 10.0 t0 = 0.320 # Create the detector detector = Detector( Panel( "", # Type "", # Name (10, 0, 0), # Fast axis (0, 10, 0), # Slow axis (0, 0, 200), # Origin (0.172, 0.172), # Pixel size (512, 512), # Image size (0, 1000), # Trusted range 0.0, # Thickness "", # Material ParallaxCorrectedPxMmStrategy(mu, t0), ) ) for i in range(10000): mm = (random.uniform(-1000, 1000), random.uniform(-1000, 1000)) px = detector[0].millimeter_to_pixel(mm) mm2 = detector[0].pixel_to_millimeter(px) assert abs(matrix.col(mm) - matrix.col(mm2)) < 1e-3
def _detector(self): d = FormatCBFMultiTileHierarchyStill._detector(self) try: # a header only CBF file will not have a beam object beam = self._beam() except Exception as e: if "CBF_NOTFOUND" not in str(e): raise e return d # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) wavelength = beam.get_wavelength() thickness = 0.5 # mm, see Hart et al. 2012 from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") # mu_at_angstrom returns cm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 # mu: mm^-1 t0 = thickness for panel in d: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) return d
def _detector(self): '''Return a model for a simple detector, presuming no one has one of these on a two-theta stage. Assert that the beam centre is provided in the Mosflm coordinate frame.''' distance = float( self._cif_header_dictionary['Detector_distance'].split()[0]) beam_xy = self._cif_header_dictionary['Beam_xy'].replace( '(', '').replace(')', '').replace(',', '').split()[:2] wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = self._cif_header_dictionary['Pixel_size'].replace( 'm', '').replace('x', '').split() pixel_x, pixel_y = map(float, pixel_xy) if 'Silicon' in self._cif_header_dictionary: thickness = float( self._cif_header_dictionary['Silicon'].split()[2]) * 1000.0 material = 'Si' elif 'CdTe' in self._cif_header_dictionary: thickness = float( self._cif_header_dictionary['CdTe'].split()[2]) * 1000.0 material = 'CdTe' else: thickness = 0.450 material = 'Si' nx = int( self._cif_header_dictionary['X-Binary-Size-Fastest-Dimension']) ny = int(self._cif_header_dictionary['X-Binary-Size-Second-Dimension']) overload = dxtbx_overload_scale * int( self._cif_header_dictionary['Count_cutoff'].split()[0]) underload = -1 # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table(material) mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness detector = self._detector_factory.simple( 'PAD', distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), '+x', '-y', (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], ParallaxCorrectedPxMmStrategy(mu, t0)) detector[0].set_thickness(thickness) detector[0].set_material(material) detector[0].set_mu(mu) return detector
def _detector(self): """ Create an Eiger detector profile (taken from FormatCBFMiniEiger) """ configuration = self.header["configuration"] info = self.header["info"] distance = configuration["detector_distance"] wavelength = configuration["wavelength"] beam_x = configuration["beam_center_x"] beam_y = configuration["beam_center_y"] pixel_x = configuration["x_pixel_size"] pixel_y = configuration["y_pixel_size"] material = configuration["sensor_material"] thickness = configuration["sensor_thickness"] * 1000 nx = configuration["x_pixels_in_detector"] ny = configuration["y_pixels_in_detector"] if "count_rate_correction_count_cutoff" in configuration: overload = configuration["count_rate_correction_count_cutoff"] else: # hard-code if missing from Eiger stream header overload = 4001400 underload = -1 try: identifier = configuration["description"] except KeyError: identifier = "Unknown Eiger" table = attenuation_coefficient.get_table(material) mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness detector = self._detector_factory.simple( sensor="PAD", distance=distance * 1000.0, beam_centre=(beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), fast_direction="+x", slow_direction="-y", pixel_size=(1000 * pixel_x, 1000 * pixel_y), image_size=(nx, ny), trusted_range=(underload, overload), mask=[], px_mm=ParallaxCorrectedPxMmStrategy(mu, t0), mu=mu, ) for f0, f1, s0, s1 in determine_eiger_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) for panel in detector: panel.set_thickness(thickness) panel.set_material(material) panel.set_identifier(identifier) panel.set_mu(mu) return detector
def _detector(self): """Return a model for a simple detector, presuming no one has one of these on a two-theta stage. Assert that the beam centre is provided in the Mosflm coordinate frame.""" distance = float( self._cif_header_dictionary["Detector_distance"].split()[0]) beam_xy = (self._cif_header_dictionary["Beam_xy"].replace( "(", "").replace(")", "").replace(",", "").split()[:2]) wavelength = float( self._cif_header_dictionary["Wavelength"].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = (self._cif_header_dictionary["Pixel_size"].replace( "m", "").replace("x", "").split()) pixel_x, pixel_y = map(float, pixel_xy) thickness = float( self._cif_header_dictionary["Silicon"].split()[2]) * 1000.0 nx = int( self._cif_header_dictionary["X-Binary-Size-Fastest-Dimension"]) ny = int(self._cif_header_dictionary["X-Binary-Size-Second-Dimension"]) overload = int(self._cif_header_dictionary["Count_cutoff"].split()[0]) underload = -1 # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness # FIXME would also be very nice to be able to take into account the # misalignment of the individual modules given the calibration information... detector = self._detector_factory.simple( "PAD", distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), "+x", "-y", (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], ParallaxCorrectedPxMmStrategy(mu, t0), ) """ for f0, f1, s0, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0-1, s0-1, f1, s1) """ return detector
def _detector(self, index=None): from dxtbx.model.detector import Detector from scitbx import matrix import math wavelength = self.get_beam(index).get_wavelength() from dxtbx.model import ParallaxCorrectedPxMmStrategy from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 px_mm = ParallaxCorrectedPxMmStrategy(mu, self.thickness) if self.RECONST_MODE: return self._detector_factory.simple( sensor="PAD", distance=self.distance, beam_centre=( self.RECONST_SIZE / 2 * self.PIXEL_SIZE, self.RECONST_SIZE / 2 * self.PIXEL_SIZE, ), fast_direction="-x", slow_direction="-y", pixel_size=(self.PIXEL_SIZE, self.PIXEL_SIZE), image_size=(self.RECONST_SIZE, self.RECONST_SIZE), trusted_range=(-1, 65535), mask=[], ) # TODO: add gaps detector = Detector() root = detector.hierarchy() root.set_frame((-1, 0, 0), (0, 1, 0), (0, 0, -self.distance)) for i in range(8): angle = math.pi * self.panel_rotations[i] / 180.0 fast = matrix.col((math.cos(angle), math.sin(angle), 0)) slow = matrix.col((-math.sin(angle), math.cos(angle), 0)) normal = fast.cross(slow) origin = (matrix.col(( -self.panel_origins[i][0], self.panel_origins[i][1], self.panel_origins[i][2], )) / 1000.0) p = root.add_panel() p.set_type("SENSOR_PAD") p.set_name("Panel%d" % i) p.set_image_size((512, 1024)) p.set_trusted_range((-1, 65535)) p.set_pixel_size((self.PIXEL_SIZE, self.PIXEL_SIZE)) p.set_thickness(self.thickness) p.set_local_frame(fast.elems, slow.elems, origin.elems) p.set_px_mm_strategy(px_mm) p.set_gain(10) return detector
def _detector(self): '''Return a working detector instance, with added mask regions.''' detector = self._detector_factory.imgCIF_H(self._get_cbf_handle(), 'PAD') for f0, s0, f1, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0, s0, f1, s1) import re m = re.search('^#\s*(\S+)\ssensor, thickness\s*([0-9.]+)\s*m\s*$', \ self._cif_header, re.MULTILINE) if m: # header gives thickness in metres, we store mm thickness = float(m.group(2)) * 1000 material = m.group(1) if material == 'Silicon': material = 'Si' for panel in detector: panel.set_thickness(thickness) panel.set_material(material) try: # a header only CBF file will not have a beam object beam = self._beam() except Exception: beam = None if beam: # attenuation coefficient depends on the beam wavelength wavelength = beam.get_wavelength() from cctbx.eltbx import attenuation_coefficient from dxtbx.model import ParallaxCorrectedPxMmStrategy # this will fail for undefined composite materials table = attenuation_coefficient.get_table(material) # mu_at_angstrom returns cm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 for panel in detector: panel.set_px_mm_strategy( ParallaxCorrectedPxMmStrategy(mu, thickness)) panel.set_mu(mu) m = re.search('^#\s*Detector:\s+(.*?)\s*$', \ self._cif_header, re.MULTILINE) if m and m.group(1): panel.set_identifier(m.group(1)) return detector
def _detector(self): distance = float( self._cif_header_dictionary['Detector_distance'].split()[0]) beam_xy = self._cif_header_dictionary['Beam_xy'].replace( '(', '').replace(')', '').replace(',', '').split()[:2] wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = self._cif_header_dictionary['Pixel_size'].replace( 'm', '').replace('x', '').split() pixel_x, pixel_y = map(float, pixel_xy) thickness = float( self._cif_header_dictionary['Silicon'].split()[-2]) * 1000.0 nx = int( self._cif_header_dictionary['X-Binary-Size-Fastest-Dimension']) ny = int( self._cif_header_dictionary['X-Binary-Size-Second-Dimension']) overload = int( self._cif_header_dictionary['Count_cutoff'].split()[0]) underload = -1 from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness detector = self._detector_factory.simple( 'PAD', distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), '+x', '-y', (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], ParallaxCorrectedPxMmStrategy(mu, t0)) for f0, s0, f1, s1 in [(0, 513, 1030, 550)]: detector[0].add_mask(f0, s0, f1, s1) return detector
class FormatCBFCspad(FormatCBFMultiTileHierarchy, FormatStill): '''An image reading class CSPAD CBF files''' @staticmethod def understand(image_file): '''Check to see if this looks like an CSPD CBF format image, i.e. we can make sense of it.''' cbf_handle = pycbf.cbf_handle_struct() cbf_handle.read_widefile(image_file, pycbf.MSG_DIGEST) cbf_handle.find_category("diffrn_detector") if cbf_handle.count_rows() > 1: return False # support 1 detector per file for now cbf_handle.find_column("type") return cbf_handle.get_value() == "CS PAD" def _detector(self): d = FormatCBFMultiTileHierarchy._detector(self) try: # a header only CBF file will not have a beam object beam = self._beam() except Exception, e: if "CBF_NOTFOUND" not in str(e): raise e return d # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) wavelength = beam.get_wavelength() thickness = 0.5 from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") # mu_at_angstrom returns cm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness for panel in d: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) return d
def _detector(self): """Return a model for the detector, allowing for two-theta offsets and the detector position. This will be rather more complex...""" detector_name = self._header_dictionary["DETECTOR_NAMES"].split()[0].strip() detector_axes = self.get_detector_axes(detector_name) fast = matrix.col(tuple(detector_axes[:3])) slow = matrix.col(tuple(detector_axes[3:])) distortion = self.get_distortion(detector_name) # multiply through by the distortion to get the true detector fast, slow detector_fast, detector_slow = ( distortion[0] * fast + distortion[1] * slow, distortion[2] * fast + distortion[3] * slow, ) beam_pixels = self.get_beam_pixels(detector_name) pixel_size = self.get_pixel_size(detector_name) image_size = self.get_image_size(detector_name) detector_origin = -( beam_pixels[0] * pixel_size[0] * detector_fast + beam_pixels[1] * pixel_size[1] * detector_slow ) gonio_axes = self.get_gonio_axes(detector_name) gonio_values = self.get_gonio_values(detector_name) gonio_units = self._header_dictionary["%sGONIO_UNITS" % detector_name].split() gonio_num_axes = int( self._header_dictionary["%sGONIO_NUM_VALUES" % detector_name] ) rotations = [] translations = [] for j, unit in enumerate(gonio_units): axis = matrix.col(gonio_axes[3 * j : 3 * (j + 1)]) if unit == "deg": rotations.append( axis.axis_and_angle_as_r3_rotation_matrix(gonio_values[j], deg=True) ) translations.append(matrix.col((0.0, 0.0, 0.0))) elif unit == "mm": rotations.append( matrix.sqr((1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0)) ) translations.append(gonio_values[j] * axis) else: raise RuntimeError("unknown axis unit %s" % unit) rotations.reverse() translations.reverse() for j in range(gonio_num_axes): detector_fast = rotations[j] * detector_fast detector_slow = rotations[j] * detector_slow detector_origin = rotations[j] * detector_origin detector_origin = translations[j] + detector_origin overload = int(float(self._header_dictionary["SATURATED_VALUE"])) underload = -1 # Unfortunately thickness and material are not stored in the header. Set # to sensible defaults. t0 = 0.450 material = "Si" table = attenuation_coefficient.get_table(material) wavelength = float(self._header_dictionary["SCAN_WAVELENGTH"]) mu = table.mu_at_angstrom(wavelength) / 10.0 detector = self._detector_factory.complex( "PAD", detector_origin.elems, detector_fast.elems, detector_slow.elems, pixel_size, image_size, (underload, overload), px_mm=ParallaxCorrectedPxMmStrategy(mu, t0), mu=mu, ) for panel in detector: panel.set_thickness(t0) panel.set_material(material) return detector
from __future__ import division import sys from dxtbx.serialize import load from dxtbx.model import ParallaxCorrectedPxMmStrategy, SimplePxMmStrategy from scitbx.array_family import flex from matplotlib import pylab convert = ParallaxCorrectedPxMmStrategy(0.252500934883) convert2 = SimplePxMmStrategy() sweep = load.imageset(sys.argv[1]) detector = sweep.get_detector() #print detector[0].pixel_to_millimeter((0, 1)) image_size = sweep[0].all() image = flex.double(flex.grid(image_size)) for j in range(image_size[0]): for i in range(image_size[1]): mm1 = convert2.to_millimeter(detector[0], (i, j)) px2 = convert.to_pixel(detector[0], mm1) image[j,i] = j - px2[1] print flex.max(image), flex.min(image) pylab.imshow(image.as_numpy_array()) pylab.show()
def _detector(self): """Return a model for a simple detector, presuming no one has one of these on a two-theta stage. Assert that the beam centre is provided in the Mosflm coordinate frame.""" if not self._multi_panel: detector = FormatCBFMini._detector(self) for f0, f1, s0, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) return detector # got to here means 60-panel version d = Detector() distance = float( self._cif_header_dictionary["Detector_distance"].split()[0]) beam_xy = (self._cif_header_dictionary["Beam_xy"].replace( "(", "").replace(")", "").replace(",", "").split()[:2]) beam_x, beam_y = map(float, beam_xy) wavelength = float( self._cif_header_dictionary["Wavelength"].split()[0]) pixel_xy = (self._cif_header_dictionary["Pixel_size"].replace( "m", "").replace("x", "").split()) pixel_x, pixel_y = map(float, pixel_xy) thickness = float( self._cif_header_dictionary["Silicon"].split()[2]) * 1000.0 nx = int( self._cif_header_dictionary["X-Binary-Size-Fastest-Dimension"]) ny = int(self._cif_header_dictionary["X-Binary-Size-Second-Dimension"]) overload = int(self._cif_header_dictionary["Count_cutoff"].split()[0]) underload = -1 # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness # FIXME would also be very nice to be able to take into account the # misalignment of the individual modules given the calibration... # single detector or multi-module detector pixel_x *= 1000.0 pixel_y *= 1000.0 distance *= 1000.0 beam_centre = matrix.col((beam_x * pixel_x, beam_y * pixel_y, 0)) fast = matrix.col((1.0, 0.0, 0.0)) slow = matrix.col((0.0, -1.0, 0.0)) s0 = matrix.col((0, 0, -1)) origin = (distance * s0) - (fast * beam_centre[0]) - (slow * beam_centre[1]) root = d.hierarchy() root.set_local_frame(fast.elems, slow.elems, origin.elems) det = _DetectorDatabase["Pilatus"] # Edge dead areas not included, only gaps between modules matter n_fast, remainder = divmod(nx, det.module_size_fast) assert (n_fast - 1) * det.gap_fast == remainder n_slow, remainder = divmod(ny, det.module_size_slow) assert (n_slow - 1) * det.gap_slow == remainder mx = det.module_size_fast my = det.module_size_slow dx = det.gap_fast dy = det.gap_slow xmins = [(mx + dx) * i for i in range(n_fast)] xmaxes = [mx + (mx + dx) * i for i in range(n_fast)] ymins = [(my + dy) * i for i in range(n_slow)] ymaxes = [my + (my + dy) * i for i in range(n_slow)] self.coords = {} fast = matrix.col((1.0, 0.0, 0.0)) slow = matrix.col((0.0, 1.0, 0.0)) panel_idx = 0 for ymin, ymax in zip(ymins, ymaxes): for xmin, xmax in zip(xmins, xmaxes): xmin_mm = xmin * pixel_x ymin_mm = ymin * pixel_y origin_panel = fast * xmin_mm + slow * ymin_mm panel_name = "Panel%d" % panel_idx panel_idx += 1 p = d.add_panel() p.set_type("SENSOR_PAD") p.set_name(panel_name) p.set_raw_image_offset((xmin, ymin)) p.set_image_size((xmax - xmin, ymax - ymin)) p.set_trusted_range((underload, overload)) p.set_pixel_size((pixel_x, pixel_y)) p.set_thickness(thickness) p.set_material("Si") p.set_mu(mu) p.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) p.set_local_frame(fast.elems, slow.elems, origin_panel.elems) p.set_raw_image_offset((xmin, ymin)) self.coords[panel_name] = (xmin, ymin, xmax, ymax) return d
def _detector(self, index=None): if index is None: index = 0 run = self.get_run_from_index(index) det = self._get_psana_detector(run) geom = det.pyda.geoaccess(run.run()) cob = read_slac_metrology(geometry=geom, include_asic_offset=True) distance = env_distance( self.params.detector_address[0], run.env(), self.params.cspad.detz_offset ) d = Detector() pg0 = d.hierarchy() # first deal with D0 det_num = 0 origin = col((cob[(0,)] * col((0, 0, 0, 1)))[0:3]) fast = col((cob[(0,)] * col((1, 0, 0, 1)))[0:3]) - origin slow = col((cob[(0,)] * col((0, 1, 0, 1)))[0:3]) - origin origin += col((0.0, 0.0, -distance)) pg0.set_local_frame(fast.elems, slow.elems, origin.elems) pg0.set_name("D%d" % (det_num)) for quad_num in range(4): # Now deal with Qx pg1 = pg0.add_group() origin = col((cob[(0, quad_num)] * col((0, 0, 0, 1)))[0:3]) fast = col((cob[(0, quad_num)] * col((1, 0, 0, 1)))[0:3]) - origin slow = col((cob[(0, quad_num)] * col((0, 1, 0, 1)))[0:3]) - origin pg1.set_local_frame(fast.elems, slow.elems, origin.elems) pg1.set_name("D%dQ%d" % (det_num, quad_num)) for sensor_num in range(8): # Now deal with Sy pg2 = pg1.add_group() origin = col((cob[(0, quad_num, sensor_num)] * col((0, 0, 0, 1)))[0:3]) fast = ( col((cob[(0, quad_num, sensor_num)] * col((1, 0, 0, 1)))[0:3]) - origin ) slow = ( col((cob[(0, quad_num, sensor_num)] * col((0, 1, 0, 1)))[0:3]) - origin ) pg2.set_local_frame(fast.elems, slow.elems, origin.elems) pg2.set_name("D%dQ%dS%d" % (det_num, quad_num, sensor_num)) # Now deal with Az for asic_num in range(2): val = "ARRAY_D0Q%dS%dA%d" % (quad_num, sensor_num, asic_num) p = pg2.add_panel() origin = col( (cob[(0, quad_num, sensor_num, asic_num)] * col((0, 0, 0, 1)))[ 0:3 ] ) fast = ( col( ( cob[(0, quad_num, sensor_num, asic_num)] * col((1, 0, 0, 1)) )[0:3] ) - origin ) slow = ( col( ( cob[(0, quad_num, sensor_num, asic_num)] * col((0, 1, 0, 1)) )[0:3] ) - origin ) p.set_local_frame(fast.elems, slow.elems, origin.elems) p.set_pixel_size( (cspad_cbf_tbx.pixel_size, cspad_cbf_tbx.pixel_size) ) p.set_image_size(cspad_cbf_tbx.asic_dimension) p.set_trusted_range( ( cspad_tbx.cspad_min_trusted_value, cspad_tbx.cspad_saturated_value, ) ) p.set_name(val) try: beam = self._beam(index) except Exception: print( "No beam object initialized. Returning CSPAD detector without parallax corrections" ) return d # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) wavelength = beam.get_wavelength() thickness = 0.5 # mm, see Hart et al. 2012 table = attenuation_coefficient.get_table("Si") # mu_at_angstrom returns cm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 # mu: mm^-1 t0 = thickness for panel in d: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) return d
def test_detector(): from dxtbx.model import ParallaxCorrectedPxMmStrategy def create_detector(offset=0): # Create the detector detector = Detector( Panel( "", # Type "Panel", # Name (10, 0, 0), # Fast axis (0, 10, 0), # Slow axis (0 + offset, 0 + offset, 200 - offset), # Origin (0.172, 0.172), # Pixel size (512, 512), # Image size (0, 1000), # Trusted range 0.1, # Thickness "Si", # Material identifier="123")) # Identifier return detector detector = create_detector() # Perform some tests tst_get_identifier(detector) tst_get_gain(detector) tst_set_mosflm_beam_centre(detector) tst_get_pixel_lab_coord(detector) tst_get_image_size_mm(detector) tst_is_value_in_trusted_range(detector) tst_is_coord_valid(detector) tst_pixel_to_millimeter_to_pixel(detector) tst_get_names(detector) tst_get_thickness(detector) tst_get_material(detector) tst_resolution(detector) tst_panel_mask() # Attenuation length from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(1) / 10.0 t0 = 0.320 # Create another detector with different origin detector_moved = create_detector(offset=100) tst_detectors_are_different(detector, detector_moved) detector_moved_copy = create_detector(offset=100) tst_detectors_are_same(detector_moved, detector_moved_copy) # Create the detector detector = Detector( Panel( "", # Type "", # Name (10, 0, 0), # Fast axis (0, 10, 0), # Slow axis (0, 0, 200), # Origin (0.172, 0.172), # Pixel size (512, 512), # Image size (0, 1000), # Trusted range 0.0, # Thickness "", # Material ParallaxCorrectedPxMmStrategy(mu, t0))) tst_parallax_correction(detector)
def _detector(self): '''Detector model, allowing for small offsets in the positions of 60 detector modules''' # Module positional offsets in x, y, in pixels - for the moment ignoring the # rotational offsets as these are not well defined. To be honest these # positional offsets are also not well defined as I do not know how they # should be applied... x = { (0, 0): -0.477546, (0, 1): 0.130578, (0, 2): 0.045041, (0, 3): -0.439872, (0, 4): -0.382077, (1, 0): 0.087405, (1, 1): 0.249597, (1, 2): 0.184265, (1, 3): 0.158342, (1, 4): 0.025225, (2, 0): -0.179892, (2, 1): -0.010974, (2, 2): -0.139207, (2, 3): 0.282851, (2, 4): -0.442219, (3, 0): -0.185027, (3, 1): 0.218601, (3, 2): 0.092585, (3, 3): 0.35862, (3, 4): -0.29161, (4, 0): 0.145368, (4, 1): 0.609289, (4, 2): 0.396265, (4, 3): 0.41625, (4, 4): 0.07152, (5, 0): 0.247142, (5, 1): 0.046563, (5, 2): 0.248714, (5, 3): -0.044628, (5, 4): -0.391509, (6, 0): 0.516643, (6, 1): 0.358453, (6, 2): 0.069219, (6, 3): 0.095861, (6, 4): -0.167403, (7, 0): -0.381352, (7, 1): -0.35338, (7, 2): 0.348656, (7, 3): 0.024543, (7, 4): 0.328706, (8, 0): 0.150886, (8, 1): 0.244987, (8, 2): -0.102911, (8, 3): 0.16633, (8, 4): 0.386622, (9, 0): 0.037924, (9, 1): 0.314392, (9, 2): 0.238818, (9, 3): 0.815028, (9, 4): -0.048818, (10, 0): -0.670524, (10, 1): -0.304119, (10, 2): 0.252284, (10, 3): -0.05485, (10, 4): -0.355264, (11, 0): -0.404947, (11, 1): -0.020622, (11, 2): 0.648473, (11, 3): -0.277175, (11, 4): -0.711951 } y = { (0, 0): -0.494797, (0, 1): -0.212976, (0, 2): 0.085351, (0, 3): 0.35494, (0, 4): 0.571189, (1, 0): -0.421708, (1, 1): 0.061914, (1, 2): 0.238996, (1, 3): 0.146692, (1, 4): 0.407145, (2, 0): -0.313212, (2, 1): -0.225025, (2, 2): 0.031613, (2, 3): -0.047839, (2, 4): 0.42716, (3, 0): -0.361193, (3, 1): 0.057663, (3, 2): 0.022357, (3, 3): 0.062717, (3, 4): 0.150611, (4, 0): 0.035511, (4, 1): -0.271567, (4, 2): 0.007761, (4, 3): -0.124021, (4, 4): 0.093017, (5, 0): -0.238897, (5, 1): -0.179724, (5, 2): -0.113608, (5, 3): 0.017841, (5, 4): -0.012933, (6, 0): -0.166337, (6, 1): -0.272922, (6, 2): -0.194665, (6, 3): -0.058535, (6, 4): -0.405404, (7, 0): -0.318824, (7, 1): -0.311276, (7, 2): -0.205223, (7, 3): -0.292664, (7, 4): -0.474762, (8, 0): -0.039504, (8, 1): -0.239887, (8, 2): -0.343485, (8, 3): -0.459429, (8, 4): -0.426901, (9, 0): -0.187805, (9, 1): 0.282727, (9, 2): -0.601164, (9, 3): -0.467605, (9, 4): -0.589271, (10, 0): 0.028311, (10, 1): -0.391571, (10, 2): -0.463112, (10, 3): -0.358092, (10, 4): -0.285396, (11, 0): 0.01863, (11, 1): -0.380099, (11, 2): -0.234953, (11, 3): -0.593992, (11, 4): -0.801247 } distance = float( self._cif_header_dictionary['Detector_distance'].split()[0]) beam_xy = self._cif_header_dictionary['Beam_xy'].replace( '(', '').replace(')', '').replace(',', '').split()[:2] beam_x, beam_y = map(float, beam_xy) wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) pixel_xy = self._cif_header_dictionary['Pixel_size'].replace( 'm', '').replace('x', '').split() pixel_x, pixel_y = map(float, pixel_xy) thickness = float( self._cif_header_dictionary['Silicon'].split()[2]) * 1000.0 nx = int( self._cif_header_dictionary['X-Binary-Size-Fastest-Dimension']) ny = int(self._cif_header_dictionary['X-Binary-Size-Second-Dimension']) overload = int(self._cif_header_dictionary['Count_cutoff'].split()[0]) underload = -1 # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness # FIXME would also be very nice to be able to take into account the # misalignment of the individual modules given the calibration... # single detector or multi-module detector pixel_x *= 1000.0 pixel_y *= 1000.0 distance *= 1000.0 if not self._multi_panel: detector = self._detector_factory.simple( 'PAD', distance, (beam_x * pixel_x, beam_y * pixel_y), '+x', '-y', (pixel_x, pixel_y), (nx, ny), (underload, overload), [], ParallaxCorrectedPxMmStrategy(mu, t0)) for f0, f1, s0, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) detector[0].set_thickness(thickness) detector[0].set_material('Si') detector[0].set_mu(mu) return detector # got to here means 60-panel version from dxtbx.model import Detector from scitbx import matrix d = Detector() beam_centre = matrix.col((beam_x * pixel_x, beam_y * pixel_y, 0)) fast = matrix.col((1.0, 0.0, 0.0)) slow = matrix.col((0.0, -1.0, 0.0)) s0 = matrix.col((0, 0, -1)) origin = (distance * s0) - (fast * beam_centre[0]) - \ (slow * beam_centre[1]) root = d.hierarchy() root.set_local_frame(fast.elems, slow.elems, origin.elems) xmins = [0, 494, 988, 1482, 1976] xmaxes = [487, 981, 1475, 1969, 2463] ymins = [ 0, 212, 424, 636, 848, 1060, 1272, 1484, 1696, 1908, 2120, 2332 ] ymaxes = [ 195, 407, 619, 831, 1043, 1255, 1467, 1679, 1891, 2103, 2315, 2527 ] self.coords = {} fast = matrix.col((1.0, 0.0, 0.0)) slow = matrix.col((0.0, 1.0, 0.0)) panel_idx = 0 for ymin, ymax in zip(ymins, ymaxes): for xmin, xmax in zip(xmins, xmaxes): xmin_mm = xmin * pixel_x ymin_mm = ymin * pixel_y origin_panel = fast * xmin_mm + slow * ymin_mm panel_name = "Panel%d" % panel_idx panel_idx += 1 p = root.add_panel() p.set_type("SENSOR_PAD") p.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) p.set_name(panel_name) p.set_image_size((xmax - xmin, ymax - ymin)) p.set_trusted_range((underload, overload)) p.set_pixel_size((pixel_x, pixel_y)) p.set_thickness(thickness) p.set_material('Si') p.set_mu(mu) p.set_local_frame(fast.elems, slow.elems, origin_panel.elems) p.set_raw_image_offset((xmin, ymin)) self.coords[panel_name] = (xmin, ymin, xmax, ymax) return d
def correct_inv(self, xy): from dxtbx.model import ParallaxCorrectedPxMmStrategy convert = ParallaxCorrectedPxMmStrategy(self.mu, self.t0) return convert.to_millimeter(self.detector[0], xy)
def _detector(self): # module positions from detector blueprints - modelling at the moment as # 24 modules, each consisting of 5 sensors (the latter is ignored) from dxtbx.model import Detector from scitbx import matrix import math x = matrix.col((-1, 0, 0)) y = matrix.col((0, 1, 0)) z = matrix.col((0, 0, 1)) beam_xy = self._cif_header_dictionary['Beam_xy'] beam_xy = beam_xy.replace('(', '').replace(')', '').replace(',', '').split()[:2] obs_beam_x, obs_beam_y = [float(f) for f in beam_xy] ideal_beam_x = 1075 ideal_beam_y = 2594 beam_shift_x = 0.172 * (ideal_beam_x - obs_beam_x) beam_shift_y = 0.172 * (ideal_beam_y - obs_beam_y) distance = float(self._cif_header_dictionary['Detector_distance']. split()[0]) * 1000.0 wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) thickness = float( self._cif_header_dictionary['Silicon'].split()[2]) * 1000.0 off_x = 184.9 detector = Detector() root = detector.hierarchy() root.set_frame(x.elems, y.elems, (-distance * z + (beam_shift_x * x) + (beam_shift_y * y)).elems) from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness px_mm = ParallaxCorrectedPxMmStrategy(mu, t0) self.coords = {} for j in range(24): shift_y = 195 + 17 ymin, ymax = j * shift_y, j * shift_y + 195 angle = math.pi * (-12.2 + 0.5 * 7.903 + j * (7.903 + 0.441)) / 180.0 fast = matrix.col((1, 0, 0)) slow = matrix.col((0, math.sin(angle), math.cos(angle))) normal = fast.cross(slow) row_origin = 250.0 * normal - off_x * fast - 16.8 * slow if not self._multi_panel: xmin, xmax = 0, 2463 # OK two calls to add_panel here for detector like things => two # copies of the panel then? https://github.com/dials/dials/issues/189 # ... this is also not the source of the leak # OBS! you need to set the panel to a root before set local frame... p = root.add_panel() p.set_type('SENSOR_PAD') p.set_name('row-%02d' % j) p.set_raw_image_offset((xmin, ymin)) p.set_image_size((2463, 195)) p.set_trusted_range((-1, 1000000)) p.set_pixel_size((0.172, 0.172)) p.set_local_frame(fast.elems, slow.elems, row_origin.elems) p.set_thickness(thickness) p.set_material('Si') p.set_mu(mu) p.set_px_mm_strategy(px_mm) p.set_raw_image_offset((xmin, ymin)) self.coords[p.get_name()] = (xmin, ymin, xmax, ymax) else: shift_x = 487 + 7 for i in range(5): xmin, xmax = i * shift_x, i * shift_x + 487 origin = row_origin + i * (487 + 7) * 0.172 * fast # OBS! you need to set the panel to a root before set local frame... p = root.add_panel() p.set_type('SENSOR_PAD') p.set_name('row-%02d-col-%02d' % (j, i)) p.set_raw_image_offset((xmin, ymin)) p.set_image_size((487, 195)) p.set_trusted_range((-1, 1000000)) p.set_pixel_size((0.172, 0.172)) p.set_local_frame(fast.elems, slow.elems, origin.elems) p.set_thickness(thickness) p.set_material('Si') p.set_mu(mu) p.set_px_mm_strategy(px_mm) p.set_raw_image_offset((xmin, ymin)) self.coords[p.get_name()] = (xmin, ymin, xmax, ymax) return detector
def _detector(self): """Return a model for a simple detector, presuming no one has one of these on a two-theta stage. Assert that the beam centre is provided in the Mosflm coordinate frame.""" distance = float(self._cif_header_dictionary["Detector_distance"].split()[0]) beam_xy = ( self._cif_header_dictionary["Beam_xy"] .replace("(", "") .replace(")", "") .replace(",", "") .split()[:2] ) wavelength = float(self._cif_header_dictionary["Wavelength"].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = ( self._cif_header_dictionary["Pixel_size"] .replace("m", "") .replace("x", "") .split() ) pixel_x, pixel_y = map(float, pixel_xy) if "Silicon" in self._cif_header_dictionary: thickness = ( float(self._cif_header_dictionary["Silicon"].split()[2]) * 1000.0 ) material = "Si" sensor = "PAD" elif "CdTe" in self._cif_header_dictionary: thickness = float(self._cif_header_dictionary["CdTe"].split()[2]) * 1000.0 material = "CdTe" sensor = "PAD" elif "CCD" in self._cif_header_dictionary: thickness = 0 material = None sensor = "CCD" else: thickness = 0 material = None sensor = None nx = int(self._cif_header_dictionary["X-Binary-Size-Fastest-Dimension"]) ny = int(self._cif_header_dictionary["X-Binary-Size-Second-Dimension"]) overload = dxtbx_overload_scale * int( self._cif_header_dictionary["Count_cutoff"].split()[0] ) underload = -1 if material is not None: # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table(material) mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness px_mm = ParallaxCorrectedPxMmStrategy(mu, t0) else: px_mm = SimplePxMmStrategy() detector = self._detector_factory.simple( sensor, distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), "+x", "-y", (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], px_mm=px_mm, ) if material is not None: detector[0].set_thickness(thickness) detector[0].set_material(material) detector[0].set_mu(mu) return detector
def _detector(self): '''Return a model for the detector, allowing for two-theta offsets and the detector position. This will be rather more complex...''' detector_name = self._header_dictionary['DETECTOR_NAMES'].split( )[0].strip() detector_axes = map( float, self._header_dictionary['%sDETECTOR_VECTORS' % detector_name].split()) fast = matrix.col(tuple(detector_axes[:3])) slow = matrix.col(tuple(detector_axes[3:])) distortion = map( int, self._header_dictionary['%sSPATIAL_DISTORTION_VECTORS' % detector_name].split()) # multiply through by the distortion to get the true detector fast, slow detector_fast, detector_slow = distortion[0] * fast + distortion[1] * slow, \ distortion[2] * fast + distortion[3] * slow beam_pixels = map( float, self._header_dictionary['%sSPATIAL_DISTORTION_INFO' % detector_name].split()[:2]) pixel_size = map( float, self._header_dictionary['%sSPATIAL_DISTORTION_INFO' % detector_name].split()[2:]) image_size = map( int, self._header_dictionary['%sDETECTOR_DIMENSIONS' % detector_name].split()) detector_origin = - (beam_pixels[0] * pixel_size[0] * detector_fast + \ beam_pixels[1] * pixel_size[1] * detector_slow) gonio_axes = map( float, self._header_dictionary['%sGONIO_VECTORS' % detector_name].split()) gonio_values = map( float, self._header_dictionary['%sGONIO_VALUES' % detector_name].split()) gonio_units = self._header_dictionary['%sGONIO_UNITS' % detector_name].split() gonio_num_axes = int(self._header_dictionary['%sGONIO_NUM_VALUES' % detector_name]) rotations = [] translations = [] for j, unit in enumerate(gonio_units): axis = matrix.col(gonio_axes[3 * j:3 * (j + 1)]) if unit == 'deg': rotations.append( axis.axis_and_angle_as_r3_rotation_matrix(gonio_values[j], deg=True)) translations.append(matrix.col((0.0, 0.0, 0.0))) elif unit == 'mm': rotations.append( matrix.sqr((1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0))) translations.append(gonio_values[j] * axis) else: raise RuntimeError('unknown axis unit %s' % unit) rotations.reverse() translations.reverse() for j in range(gonio_num_axes): detector_fast = rotations[j] * detector_fast detector_slow = rotations[j] * detector_slow detector_origin = rotations[j] * detector_origin detector_origin = translations[j] + detector_origin overload = int(float(self._header_dictionary['SATURATED_VALUE'])) underload = -1 # Unfortunately thickness and material are not stored in the header. Set # to sensible defaults. t0 = 0.450 material = 'Si' from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table(material) wavelength = float(self._header_dictionary['SCAN_WAVELENGTH']) mu = table.mu_at_angstrom(wavelength) / 10.0 detector = self._detector_factory.complex( 'PAD', detector_origin.elems, detector_fast.elems, detector_slow.elems, pixel_size, image_size, (underload, overload), px_mm=ParallaxCorrectedPxMmStrategy(mu, t0), mu=mu) for panel in detector: panel.set_thickness(t0) panel.set_material(material) return detector
def _detector(self, index=None): import psana from xfel.cftbx.detector.cspad_cbf_tbx import read_slac_metrology from dxtbx.model import Detector from scitbx.matrix import col from dxtbx.model import ParallaxCorrectedPxMmStrategy if index is None: index = 0 self._env = self._ds.env() self._det = psana.Detector(self._src, self._env) geom = self._det.pyda.geoaccess(self.events_list[index]) cob = read_slac_metrology(geometry=geom, include_asic_offset=True) d = Detector() pg0 = d.hierarchy() # first deal with D0 det_num = 0 origin = col((cob[(0, )] * col((0, 0, 0, 1)))[0:3]) fast = col((cob[(0, )] * col((1, 0, 0, 1)))[0:3]) - origin slow = col((cob[(0, )] * col((0, 1, 0, 1)))[0:3]) - origin origin += col((0., 0., -100.)) pg0.set_local_frame(fast.elems, slow.elems, origin.elems) pg0.set_name('D%d' % (det_num)) for quad_num in xrange(4): # Now deal with Qx pg1 = pg0.add_group() origin = col((cob[(0, quad_num)] * col((0, 0, 0, 1)))[0:3]) fast = col((cob[(0, quad_num)] * col((1, 0, 0, 1)))[0:3]) - origin slow = col((cob[(0, quad_num)] * col((0, 1, 0, 1)))[0:3]) - origin pg1.set_local_frame(fast.elems, slow.elems, origin.elems) pg1.set_name('D%dQ%d' % (det_num, quad_num)) for sensor_num in xrange(8): # Now deal with Sy pg2 = pg1.add_group() origin = col((cob[(0, quad_num, sensor_num)] * col( (0, 0, 0, 1)))[0:3]) fast = col((cob[(0, quad_num, sensor_num)] * col( (1, 0, 0, 1)))[0:3]) - origin slow = col((cob[(0, quad_num, sensor_num)] * col( (0, 1, 0, 1)))[0:3]) - origin pg2.set_local_frame(fast.elems, slow.elems, origin.elems) pg2.set_name('D%dQ%dS%d' % (det_num, quad_num, sensor_num)) # Now deal with Az for asic_num in xrange(2): val = 'ARRAY_D0Q%dS%dA%d' % (quad_num, sensor_num, asic_num) p = pg2.add_panel() origin = col( (cob[(0, quad_num, sensor_num, asic_num)] * col( (0, 0, 0, 1)))[0:3]) fast = col((cob[(0, quad_num, sensor_num, asic_num)] * col( (1, 0, 0, 1)))[0:3]) - origin slow = col((cob[(0, quad_num, sensor_num, asic_num)] * col( (0, 1, 0, 1)))[0:3]) - origin p.set_local_frame(fast.elems, slow.elems, origin.elems) p.set_pixel_size( (cspad_cbf_tbx.pixel_size, cspad_cbf_tbx.pixel_size)) p.set_image_size(cspad_cbf_tbx.asic_dimension) p.set_trusted_range((cspad_tbx.cspad_min_trusted_value, cspad_tbx.cspad_saturated_value)) p.set_name(val) try: beam = self._beam(index) except Exception: print 'No beam object initialized. Returning CSPAD detector without parallax corrections' return d # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) wavelength = beam.get_wavelength() thickness = 0.5 # mm, see Hart et al. 2012 from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") # mu_at_angstrom returns cm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 # mu: mm^-1 t0 = thickness for panel in d: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) return d
def _detector(self, index=None): from PSCalib.SegGeometryStore import sgs from xfel.cftbx.detector.cspad_cbf_tbx import basis_from_geo run = self.get_run_from_index(index) if run.run() in self._cached_detector: return self._cached_detector[run.run()] if index is None: index = 0 self._env = self._ds.env() assert len(self.params.detector_address) == 1 self._det = psana.Detector(self.params.detector_address[0], self._env) geom = self._det.pyda.geoaccess(self._get_event(index).run()) pixel_size = (self._det.pixel_size(self._get_event(index)) / 1000.0 ) # convert to mm d = Detector() pg0 = d.hierarchy() # first deal with D0 det_num = 0 root = geom.get_top_geo() root_basis = basis_from_geo(root) while len(root.get_list_of_children()) == 1: sub = root.get_list_of_children()[0] sub_basis = basis_from_geo(sub) root = sub root_basis = root_basis * sub_basis t = root_basis.translation root_basis.translation = col((t[0], t[1], -t[2])) origin = col((root_basis * col((0, 0, 0, 1)))[0:3]) fast = col((root_basis * col((1, 0, 0, 1)))[0:3]) - origin slow = col((root_basis * col((0, 1, 0, 1)))[0:3]) - origin pg0.set_local_frame(fast.elems, slow.elems, origin.elems) pg0.set_name("D%d" % (det_num)) # Now deal with modules for module_num in range(len(root.get_list_of_children())): module = root.get_list_of_children()[module_num] module_basis = basis_from_geo(module) origin = col((module_basis * col((0, 0, 0, 1)))[0:3]) fast = col((module_basis * col((1, 0, 0, 1)))[0:3]) - origin slow = col((module_basis * col((0, 1, 0, 1)))[0:3]) - origin pg1 = pg0.add_group() pg1.set_local_frame(fast.elems, slow.elems, origin.elems) pg1.set_name("D%dM%d" % (det_num, module_num)) # Read the known layout of the Jungfrau 2x4 module sg = sgs.Create(segname=module.oname) xx, yy = sg.get_seg_xy_maps_um() xx = xx / 1000 yy = yy / 1000 # Now deal with ASICs for asic_num in range(8): val = "ARRAY_D0M%dA%d" % (module_num, asic_num) dim_slow = xx.shape[0] dim_fast = xx.shape[1] sensor_id = asic_num // 4 # There are 2X4 asics per module asic_in_sensor_id = asic_num % 4 # this number will be 0,1,2 or 3 id_slow = sensor_id * (dim_slow // 2) id_fast = asic_in_sensor_id * (dim_fast // 4) origin = col((xx[id_slow][id_fast], yy[id_slow][id_fast], 0)) fp = col( (xx[id_slow][id_fast + 1], yy[id_slow][id_fast + 1], 0)) sp = col( (xx[id_slow + 1][id_fast], yy[id_slow + 1][id_fast], 0)) fast = (fp - origin).normalize() slow = (sp - origin).normalize() p = pg1.add_panel() p.set_local_frame(fast.elems, slow.elems, origin.elems) p.set_pixel_size((pixel_size, pixel_size)) p.set_trusted_range((-10, 2e6)) p.set_name(val) thickness, material = 0.32, "Si" p.set_thickness(thickness) # mm p.set_material(material) # Compute the attenuation coefficient. # This will fail for undefined composite materials # mu_at_angstrom returns cm^-1, but need mu in mm^-1 table = attenuation_coefficient.get_table(material) wavelength = self.get_beam(index).get_wavelength() mu = table.mu_at_angstrom(wavelength) / 10.0 p.set_mu(mu) p.set_px_mm_strategy( ParallaxCorrectedPxMmStrategy(mu, thickness)) if (self.params.jungfrau.use_big_pixels and os.environ.get("DONT_USE_BIG_PIXELS_JUNGFRAU") is None): p.set_image_size((1030, 514)) break else: p.set_image_size((dim_fast // 4, dim_slow // 2)) if (self.params.jungfrau.use_big_pixels and os.environ.get("DONT_USE_BIG_PIXELS_JUNGFRAU") is None): assert len(d) == 8 self._cached_detector[run.run()] = d return d
def _detector(self): distance = float( self._cif_header_dictionary['Detector_distance'].split()[0]) beam_xy = self._cif_header_dictionary['Beam_xy'].replace( '(', '').replace(')', '').replace(',', '').split()[:2] wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = self._cif_header_dictionary['Pixel_size'].replace( 'm', '').replace('x', '').split() pixel_x, pixel_y = map(float, pixel_xy) # extract sensor thickness, assume < 10mm; default to 450 microns unfound try: thickness = float( self._cif_header_dictionary['Silicon'].split()[-2]) * 1000.0 material = 'Si' if thickness > 10: thickness = thickness / 1000.0 except KeyError: thickness = 0.450 material = 'Si' nx = int( self._cif_header_dictionary['X-Binary-Size-Fastest-Dimension']) ny = int(self._cif_header_dictionary['X-Binary-Size-Second-Dimension']) if 'Count_cutoff' in self._cif_header_dictionary: overload = int( self._cif_header_dictionary['Count_cutoff'].split()[0]) else: # missing from data transformed with GPhL converter - dials#376 overload = 100000000 underload = -1 try: identifier = self._cif_header_dictionary['Detector'] except KeyError: identifier = 'Unknown Eiger' from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness detector = self._detector_factory.simple( 'PAD', distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), '+x', '-y', (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], ParallaxCorrectedPxMmStrategy(mu, t0)) for f0, f1, s0, s1 in determine_eiger_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) for panel in detector: panel.set_thickness(thickness) panel.set_material(material) panel.set_identifier(identifier) panel.set_mu(mu) return detector
def _detector(self): '''Return a working detector instance, with added mask regions.''' cif_header = FormatCBFFullPilatus.get_cbf_header(self._image_file) self._cif_header_dictionary = {} for record in cif_header.split('\n'): if not '#' in record[:1]: continue if len(record[1:].split()) <= 2 and record.count(':') == 2: self._cif_header_dictionary['timestamp'] = record[1:].strip() continue tokens = record.replace('=', '').replace(':', '').split()[1:] self._cif_header_dictionary[tokens[0]] = ' '.join(tokens[1:]) for record in self._mime_header.split('\n'): if not record.strip(): continue token, value = record.split(':') self._cif_header_dictionary[token.strip()] = value.strip() distance = float( self._cif_header_dictionary['Detector_distance'].split()[0]) beam_xy = self._cif_header_dictionary['Beam_xy'].replace( '(', '').replace(')', '').replace(',', '').split()[:2] wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = self._cif_header_dictionary['Pixel_size'].replace( 'm', '').replace('x', '').split() pixel_x, pixel_y = map(float, pixel_xy) nx = int( self._cif_header_dictionary['X-Binary-Size-Fastest-Dimension']) ny = int(self._cif_header_dictionary['X-Binary-Size-Second-Dimension']) overload = int(self._cif_header_dictionary['Count_cutoff'].split()[0]) underload = -1 two_theta = float( self._cif_header_dictionary['Detector_2theta'].split()[0]) # hard code "correct" values for these distance = 0.11338 beam_x = 222.0 beam_y = 383.0 detector = self._detector_factory.two_theta( 'PAD', distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), '+x', '-y', '+x', two_theta, (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], None) import re m = re.search('^#\s*(\S+)\ssensor, thickness\s*([0-9.]+)\s*m\s*$', \ self._cif_header, re.MULTILINE) if m: # header gives thickness in metres, we store mm thickness = float(m.group(2)) * 1000 material = m.group(1) if material == 'Silicon': material = 'Si' for panel in detector: panel.set_thickness(thickness) panel.set_material(material) try: # a header only CBF file will not have a beam object beam = self._beam() except Exception: pass if beam: # attenuation coefficient depends on the beam wavelength wavelength = beam.get_wavelength() from cctbx.eltbx import attenuation_coefficient from dxtbx.model import ParallaxCorrectedPxMmStrategy # this will fail for undefined composite materials table = attenuation_coefficient.get_table(material) # mu_at_angstrom returns cm^-1, but need mu in mm^-1 mu = table.mu_at_angstrom(wavelength) / 10.0 for panel in detector: panel.set_mu(mu) panel.set_px_mm_strategy( ParallaxCorrectedPxMmStrategy(mu, thickness)) return detector
def _detector(self): '''Detector model, allowing for small offsets in the positions of 60 detector modules''' # Module positional offsets in x, y, in pixels - for the moment ignoring the # rotational offsets as these are not well defined. To be honest these # positional offsets are also not well defined as I do not know how they # should be applied... x = { (0, 0): -0.477546, (0, 1): 0.130578, (0, 2): 0.045041, (0, 3): -0.439872, (0, 4): -0.382077, (1, 0): 0.087405, (1, 1): 0.249597, (1, 2): 0.184265, (1, 3): 0.158342, (1, 4): 0.025225, (2, 0): -0.179892, (2, 1): -0.010974, (2, 2): -0.139207, (2, 3): 0.282851, (2, 4): -0.442219, (3, 0): -0.185027, (3, 1): 0.218601, (3, 2): 0.092585, (3, 3): 0.35862, (3, 4): -0.29161, (4, 0): 0.145368, (4, 1): 0.609289, (4, 2): 0.396265, (4, 3): 0.41625, (4, 4): 0.07152, (5, 0): 0.247142, (5, 1): 0.046563, (5, 2): 0.248714, (5, 3): -0.044628, (5, 4): -0.391509, (6, 0): 0.516643, (6, 1): 0.358453, (6, 2): 0.069219, (6, 3): 0.095861, (6, 4): -0.167403, (7, 0): -0.381352, (7, 1): -0.35338, (7, 2): 0.348656, (7, 3): 0.024543, (7, 4): 0.328706, (8, 0): 0.150886, (8, 1): 0.244987, (8, 2): -0.102911, (8, 3): 0.16633, (8, 4): 0.386622, (9, 0): 0.037924, (9, 1): 0.314392, (9, 2): 0.238818, (9, 3): 0.815028, (9, 4): -0.048818, (10, 0): -0.670524, (10, 1): -0.304119, (10, 2): 0.252284, (10, 3): -0.05485, (10, 4): -0.355264, (11, 0): -0.404947, (11, 1): -0.020622, (11, 2): 0.648473, (11, 3): -0.277175, (11, 4): -0.711951 } y = { (0, 0): -0.494797, (0, 1): -0.212976, (0, 2): 0.085351, (0, 3): 0.35494, (0, 4): 0.571189, (1, 0): -0.421708, (1, 1): 0.061914, (1, 2): 0.238996, (1, 3): 0.146692, (1, 4): 0.407145, (2, 0): -0.313212, (2, 1): -0.225025, (2, 2): 0.031613, (2, 3): -0.047839, (2, 4): 0.42716, (3, 0): -0.361193, (3, 1): 0.057663, (3, 2): 0.022357, (3, 3): 0.062717, (3, 4): 0.150611, (4, 0): 0.035511, (4, 1): -0.271567, (4, 2): 0.007761, (4, 3): -0.124021, (4, 4): 0.093017, (5, 0): -0.238897, (5, 1): -0.179724, (5, 2): -0.113608, (5, 3): 0.017841, (5, 4): -0.012933, (6, 0): -0.166337, (6, 1): -0.272922, (6, 2): -0.194665, (6, 3): -0.058535, (6, 4): -0.405404, (7, 0): -0.318824, (7, 1): -0.311276, (7, 2): -0.205223, (7, 3): -0.292664, (7, 4): -0.474762, (8, 0): -0.039504, (8, 1): -0.239887, (8, 2): -0.343485, (8, 3): -0.459429, (8, 4): -0.426901, (9, 0): -0.187805, (9, 1): 0.282727, (9, 2): -0.601164, (9, 3): -0.467605, (9, 4): -0.589271, (10, 0): 0.028311, (10, 1): -0.391571, (10, 2): -0.463112, (10, 3): -0.358092, (10, 4): -0.285396, (11, 0): 0.01863, (11, 1): -0.380099, (11, 2): -0.234953, (11, 3): -0.593992, (11, 4): -0.801247 } distance = float( self._cif_header_dictionary['Detector_distance'].split()[0]) beam_xy = self._cif_header_dictionary['Beam_xy'].replace( '(', '').replace(')', '').replace(',', '').split()[:2] beam_x, beam_y = map(float, beam_xy) wavelength = float( self._cif_header_dictionary['Wavelength'].split()[0]) pixel_xy = self._cif_header_dictionary['Pixel_size'].replace( 'm', '').replace('x', '').split() pixel_x, pixel_y = map(float, pixel_xy) thickness = float( self._cif_header_dictionary['Silicon'].split()[2]) * 1000.0 nx = int( self._cif_header_dictionary['X-Binary-Size-Fastest-Dimension']) ny = int(self._cif_header_dictionary['X-Binary-Size-Second-Dimension']) overload = int(self._cif_header_dictionary['Count_cutoff'].split()[0]) underload = -1 # take into consideration here the thickness of the sensor also the # wavelength of the radiation (which we have in the same file...) from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness # FIXME would also be very nice to be able to take into account the # misalignment of the individual modules given the calibration information... detector = self._detector_factory.simple( 'PAD', distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), '+x', '-y', (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], ParallaxCorrectedPxMmStrategy(mu, t0)) for f0, f1, s0, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) return detector
def _detector(self): distance = float(self._cif_header_dictionary["Detector_distance"].split()[0]) beam_xy = ( self._cif_header_dictionary["Beam_xy"] .replace("(", "") .replace(")", "") .replace(",", "") .split()[:2] ) wavelength = float(self._cif_header_dictionary["Wavelength"].split()[0]) beam_x, beam_y = map(float, beam_xy) pixel_xy = ( self._cif_header_dictionary["Pixel_size"] .replace("m", "") .replace("x", "") .split() ) pixel_x, pixel_y = map(float, pixel_xy) if "Silicon" in self._cif_header_dictionary: thickness = ( float(self._cif_header_dictionary["Silicon"].split()[2]) * 1000.0 ) material = "Si" elif "CdTe" in self._cif_header_dictionary: thickness = float(self._cif_header_dictionary["CdTe"].split()[2]) * 1000.0 material = "CdTe" else: thickness = 0.450 material = "Si" nx = int(self._cif_header_dictionary["X-Binary-Size-Fastest-Dimension"]) ny = int(self._cif_header_dictionary["X-Binary-Size-Second-Dimension"]) if "Count_cutoff" in self._cif_header_dictionary: overload = int(self._cif_header_dictionary["Count_cutoff"].split()[0]) else: # missing from data transformed with GPhL converter - dials#376 overload = 100000000 underload = -1 try: identifier = self._cif_header_dictionary["Detector"] except KeyError: identifier = "Unknown Eiger" from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table(material) mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = thickness detector = self._detector_factory.simple( "PAD", distance * 1000.0, (beam_x * pixel_x * 1000.0, beam_y * pixel_y * 1000.0), "+x", "-y", (1000 * pixel_x, 1000 * pixel_y), (nx, ny), (underload, overload), [], px_mm=ParallaxCorrectedPxMmStrategy(mu, t0), mu=mu, ) for f0, f1, s0, s1 in determine_eiger_mask(detector): detector[0].add_mask(f0 - 1, s0 - 1, f1, s1) for panel in detector: panel.set_thickness(thickness) panel.set_material(material) panel.set_identifier(identifier) panel.set_mu(mu) return detector