def tst_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 return detector detector = create_detector() # Perform some tests 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) # Attenuation length from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(1) / 10 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): """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 to_imageset(input_filename, extra_filename=None): '''Get an image set from the xds input filename plus an extra filename Params: input_filename The XDS.INP file extra_filename A (G)XPARM.XDS, INTGRATE.HKL or XDS_ASCII.HKL file Returns: The imageset ''' from iotbx.xds import xds_inp from dxtbx.imageset import ImageSetFactory import dxtbx # Read the input filename handle = xds_inp.reader() handle.read_file(input_filename) # Get the template template = handle.name_template_of_data_frames[0].replace('?', '#') image_range = handle.data_range detector_name = handle.detector if extra_filename is not None: # we can get all the extra dxtbx models from extra_filename check_format = False else: # we need the image files present to get the dxtbx models check_format = True # Create the imageset imageset = ImageSetFactory.from_template( template, image_range=image_range, check_format=False)[0] # If an extra filename has been specified, try to load models if extra_filename: models = dxtbx.load(extra_filename) detector = models.get_detector() if detector_name.strip() == 'PILATUS': from dxtbx.model import ParallaxCorrectedPxMmStrategy from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") wavelength = models.get_beam().get_wavelength() mu = table.mu_at_angstrom(wavelength) / 10.0 t0 = handle.sensor_thickness for panel in detector: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, t0)) panel.set_trusted_range( (handle.minimum_valid_pixel_value, handle.overload)) imageset.set_beam(models.get_beam()) imageset.set_detector(detector) imageset.set_goniometer(models.get_goniometer()) # take the image range from XDS.INP scan = models.get_scan() scan.set_image_range(image_range) imageset.set_scan(scan) # Return the imageset return imageset
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): 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 tst_for_z(z): from cctbx.eltbx import attenuation_coefficient eps = 1e-7 # Get the table table = attenuation_coefficient.get_table(z) # Get list of energies and coefficients energy = table.energy()[1:-1] mu_rho = table.mu_rho()[1:-1] mu_en_rho = table.mu_en_rho()[1:-1] # Check values at measured energies match for i in range(len(energy)): e = energy[i] + 1e-16 # move beyond edges mr = mu_rho[i] mer = mu_en_rho[i] if i < len(energy) - 1: if abs(e - energy[i + 1]) < eps: continue mr2 = table.mu_rho_at_ev(e * 1000000.0) mer2 = table.mu_en_rho_at_ev(e * 1000000.0) # print "%3d, %3d, %15f MeV -- %15f %15f %15f %15f -- %15.9f %15.9f" % (z, i, e, mr, mr2, mer, mer2, abs(mr - mr2), abs(mer - mer2)) assert (abs(mr - mr2) <= eps) assert (abs(mer - mer2) <= eps) print 'OK'
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
class Test(object): def __init__(self): import os import libtbx.load_env try: dials_regression = libtbx.env.dist_path('dials_regression') except KeyError, e: print 'FAIL: dials_regression not configured' exit(0) filename = os.path.join(dials_regression, 'image_examples', 'XDS', 'XPARM.XDS') import dxtbx models = dxtbx.load(filename) self.detector = models.get_detector() self.beam = models.get_beam() assert (len(self.detector) == 1) self.t0 = 0.320 from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") self.mu = table.mu_at_angstrom(self.beam.get_wavelength()) / 10.0 self.distance = self.detector[0].get_distance() self.origin = self.detector[0].get_ray_intersection( self.detector[0].get_normal())[1] self.pixel_size = self.detector[0].get_pixel_size()
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 test(self, dials_regression): filename = os.path.join(dials_regression, "image_examples", "XDS", "XPARM.XDS") import dxtbx models = dxtbx.load(filename) self.detector = models.get_detector() self.beam = models.get_beam() assert len(self.detector) == 1 self.t0 = 0.320 from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") self.mu = table.mu_at_angstrom(self.beam.get_wavelength()) / 10.0 self.distance = self.detector[0].get_distance() self.origin = self.detector[0].get_ray_intersection( self.detector[0].get_normal())[1] self.pixel_size = self.detector[0].get_pixel_size() from random import uniform # Generate some random coordinates and do the correction random_coord = lambda: (uniform(-1000, 1000), uniform(-1000, 1000)) for i in range(10000): xy = random_coord() self.tst_single(xy) from scitbx.array_family import flex xy = flex.vec2_double([random_coord() for i in range(100)]) self.tst_array(xy) self.tst_inverted_axis()
def tst_for_z(z): from cctbx.eltbx import attenuation_coefficient eps = 1e-7 # Get the table table = attenuation_coefficient.get_table(z) # Get list of energies and coefficients energy = table.energy()[1:-1] mu_rho = table.mu_rho()[1:-1] mu_en_rho = table.mu_en_rho()[1:-1] # Check values at measured energies match for i in range(len(energy)): e = energy[i] + 1e-16 # move beyond edges mr = mu_rho[i] mer = mu_en_rho[i] if i < len(energy) - 1: if abs(e - energy[i+1]) < eps: continue mr2 = table.mu_rho_at_ev(e * 1000000.0) mer2 = table.mu_en_rho_at_ev(e * 1000000.0) # print "%3d, %3d, %15f MeV -- %15f %15f %15f %15f -- %15.9f %15.9f" % (z, i, e, mr, mr2, mer, mer2, abs(mr - mr2), abs(mer - mer2)) assert(abs(mr - mr2) <= eps) assert(abs(mer - mer2) <= eps) print 'OK'
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) 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): """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 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 = 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("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 determine_pilatus_mask(detector): detector[0].add_mask(f0, s0, f1, s1) detector[0].set_thickness(thickness) detector[0].set_material('Si') detector[0].set_mu(mu) 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): '''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 model(dials_regression): filename = os.path.join(dials_regression, "image_examples", "XDS", "XPARM.XDS") models = dxtbx.load(filename) detector = models.get_detector() assert len(detector) == 1 detector = detector[0] t0 = 0.320 table = attenuation_coefficient.get_table("Si") mu = table.mu_at_angstrom(models.get_beam().get_wavelength()) / 10.0 pixel_size = detector.get_pixel_size() return {"mu": mu, "t0": t0, "detector": detector, "pixel_size": pixel_size}
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.e-3 * detector['x_pixel_size'].value, \ 1.e-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. 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): 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 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): # 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 override_detector(self, detector, params, beam, beam_params): ''' Override the detector parameters ''' # need to gather material from multiple phil parameters to set frame_hash = { } for panel_params in params.panel: panel_id = panel_params.id panel = detector[panel_params.id] if not panel_id in frame_hash: frame_hash[panel_id] = {'fast_axis':None, 'slow_axis':None, 'origin':None} if panel_params.fast_axis is not None: frame_hash[panel_id]['fast_axis'] = panel_params.fast_axis if panel_params.slow_axis is not None: frame_hash[panel_id]['slow_axis'] = panel_params.slow_axis if panel_params.origin is not None: frame_hash[panel_id]['origin'] = panel_params.origin if panel_params.name is not None: panel.set_name(panel_params.name) if panel_params.type is not None: panel.set_type(panel_params.type) if panel_params.pixel_size is not None: panel.set_pixel_size(panel_params.pixel_size) if panel_params.image_size is not None: panel.set_image_size(panel_params.image_size) if panel_params.trusted_range is not None: panel.set_trusted_range(panel_params.trusted_range) if panel_params.thickness is not None: panel.set_thickness(panel_params.thickness) if panel_params.material is not None: panel.set_material(panel_params.material) # Update the parallax correction if (panel_params.material is not None or panel_params.thickness is not None or beam_params.wavelength is not None): from dxtbx.model import ParallaxCorrectedPxMmStrategy from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table(panel.get_material()) mu = table.mu_at_angstrom(beam.get_wavelength()) / 10.0 t0 = panel.get_thickness() px_mm = ParallaxCorrectedPxMmStrategy(mu, t0) panel.set_px_mm_strategy(px_mm) panel.set_mu(mu) for panel_id in frame_hash: fast_axis = frame_hash[panel_id]['fast_axis'] slow_axis = frame_hash[panel_id]['slow_axis'] origin = frame_hash[panel_id]['origin'] if [fast_axis, slow_axis, origin].count(None) == 0: panel = detector[panel_id] panel.set_frame(fast_axis, slow_axis, origin) elif [fast_axis, slow_axis, origin].count(None) != 3: raise Sorry("fast_axis, slow_axis and origin must be set together")
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.detector import HierarchicalDetector from scitbx import matrix import math x = matrix.col((1, 0, 0)) y = matrix.col((0, 1, 0)) z = matrix.col((0, 0, 1)) obs_beam_y = 2587 ideal_beam_y = 2594 beam_shift_y = 0.172 * (2594 - 2587) 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 detector = HierarchicalDetector() root = detector.hierarchy() root.set_frame( (1, 0, 0), (0, 1, 0), (0, 0, - (250 + distance))) 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) for j in range(24): 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) # for longer wavelength data sets move 192.3 below to 184.9 if wavelength < 1.128: off_x = 191.9 else: off_x = 184.9 if group_rows: origin = 250.0 * normal - off_x * fast - 16.8 * slow + 250 * z + \ beam_shift_y * y p = detector.add_panel() # OBS! you need to set the panel to a root before set local frame... root.add_panel(p) p.set_name('row-%02d' % j) 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, origin.elems) p.set_px_mm_strategy(px_mm) else: row_origin = 250.0 * normal - off_x * fast - 16.8 * slow + 250 * z + \ beam_shift_y * y for i in range(5): p = detector.add_panel() origin = row_origin + i * (487+7) * 0.172 * fast # OBS! you need to set the panel to a root before set local frame... root.add_panel(p) p.set_name('row-%02d' % j) 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_px_mm_strategy(px_mm) 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.""" 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): """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
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, s0, f1, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0, s0, f1, s1) return detector
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 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): '''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 _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)) obs_beam_y = 2587 ideal_beam_y = 2594 beam_shift_y = 0.172 * (2594 - 2587) 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 # for longer wavelength data sets move 192.3 below to 184.9 if wavelength < 1.128: off_x = 191.9 else: off_x = 184.9 z += beam_shift_y * y detector = Detector() root = detector.hierarchy() root.set_frame( x.elems, y.elems, (-distance * z).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, 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
output { experiments = None .type = path .help = "The output experiment list file name. If None, don't output an " \ "experiment list file." reflections = corrected.refl .type = path .help = "The output reflection table file." log = dials.anvil_correction.log .type = path } """) # Get the tabulated NIST mass attenuation coefficient data for carbon. carbon_attenuation_data = attenuation_coefficient.get_table("C") def goniometer_rotation(experiment: Experiment, reflections: flex.reflection_table) -> Rotation: """ Calculate the goniometer rotation operator for each reflection. Following the DXTBX model of a goniometer, whereby a scan is only possible around one physical axis at a time, the rotation operation (conventionally denoted R, here denoted R' to avoid confusion with the notation of dxtbx/model/goniometer.h) can be calculated as R' = S · R · F. Here: * S is the 'setting rotation', the operator denoting the position of all parent axes of the scan axis, which hence defines the orientation of the scan axis; * R is the the operator denoting the scan rotation as if it were performed with
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, 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): 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 run_sim(seed=1, wavelength=0.9, distance=500, random_orientation=False, phi=0, osc=0): SIM = nanoBragg(detpixels_slowfast=(2527, 2463), pixel_size_mm=0.172, Ncells_abc=(15, 15, 15), verbose=0) # get same noise each time this test is run SIM.seed = seed # user may select random orientation if random_orientation: SIM.randomize_orientation() else: # fixed orientation, for doing rotation data sets SIM.missets_deg = (10, 20, 30) SIM.distance_mm = distance SIM.wavelength_A = wavelength # option to render a phi swell SIM.phi_deg = phi SIM.osc_deg = osc if osc >= 0: # need quite a few steps/deg for decent Rmeas SIM.phistep_deg = 0.001 SIM.oversample = 1 SIM.polarization = 1 # this will become F000, marking the beam center SIM.F000 = 100 print "mosaic_seed=", SIM.mosaic_seed print "seed=", SIM.seed print "calib_seed=", SIM.calib_seed print "missets_deg =", SIM.missets_deg # re-set the detector to be in SIM.beamcenter_convention = convention.DIALS SIM.beam_center_mm = (211, 214) sfall = fcalc_from_pdb(resolution=1.6, algorithm="direct", wavelength=SIM.wavelength_A) # use crystal structure to initialize Fhkl array SIM.Fhkl = sfall # fastest option, least realistic SIM.xtal_shape = shapetype.Tophat # only really useful for long runs SIM.progress_meter = False # prints out value of one pixel only. will not render full image! #SIM.printout_pixel_fastslow=(500,500) #SIM.printout=True # flux is always in photons/s SIM.flux = 1e12 # assumes round beam SIM.beamsize_mm = 0.1 SIM.exposure_s = 0.1 # Pilatus detector is thick SIM.detector_thick_mm = 0.450 # get attenuation depth in mm from cctbx.eltbx import attenuation_coefficient table = attenuation_coefficient.get_table("Si") SIM.detector_attenuation_length_mm = 10.0 / (table.mu_at_angstrom( SIM.wavelength_A)) print "detector_attenuation_length =", SIM.detector_attenuation_length_mm #SIM.detector_thick_mm=0 SIM.detector_thicksteps = 3 # # will simulate module mis-alignment by adjusting beam center of each module beam_center0 = SIM.beam_center_mm # simulated crystal is only 3375 unit cells (42 nm wide) # amplify spot signal to simulate physical crystal of 100 um # this is equivalent to adding up 28e9 mosaic domains in exactly the same orientation, but a lot faster aggregate_xtal_volume_mm3 = 0.1 * 0.1 * 0.1 mosaic_domain_volume_mm3 = SIM.xtal_size_mm[0] * SIM.xtal_size_mm[ 1] * SIM.xtal_size_mm[2] SIM.spot_scale = aggregate_xtal_volume_mm3 / mosaic_domain_volume_mm3 # # option for detector rotations to be about beam center or sample position #SIM.detector_pivot=pivot.Sample SIM.detector_pivot = pivot.Beam print "pivoting detector about", SIM.detector_pivot # make the module roi list # and also make up per-module mis-alignments shifts and rotations import random # make sure shifts are the same from image to image random.seed(12345) module_size = (487, 195) gap_size = (7, 17) modules = [] for mx in range(0, 5): for my in range(0, 12): # define region of interest for this module fmin = mx * (module_size[0] + gap_size[0]) fmax = fmin + module_size[0] smin = my * (module_size[1] + gap_size[1]) smax = smin + module_size[1] # assume misalignments are uniform # up to ~0.5 pixel and 0.0 deg in each direction dx = 0.07 * (random.random() - 0.5) * 2 dy = 0.07 * (random.random() - 0.5) * 2 rotx = 0.0 * (random.random() - 0.5) * 2 roty = 0.0 * (random.random() - 0.5) * 2 rotz = 0.0 * (random.random() - 0.5) * 2 modules.append( (((fmin, fmax), (smin, smax)), (dx, dy), (rotx, roty, rotz))) # # defeat module-by-module rendering by uncommenting next 3 lines #dim=SIM.detpixels_fastslow #modules = [(( ((0,dim[0]),(0,dim[1])) ),(0,0),(0,0,0))] #print modules i = -1 for roi, (dx, dy), drot in modules: i = i + 1 print "rendering module:", i, "roi=", roi SIM.region_of_interest = roi # shift the beam center x = beam_center0[0] + dx y = beam_center0[1] + dy SIM.beam_center_mm = (x, y) # also tilt the module by up to 0.05 deg in all directions SIM.detector_rotation_deg = drot print "beam center: ", SIM.beam_center_mm, "rotations:", drot # now actually burn up some CPU SIM.add_nanoBragg_spots() # add water contribution SIM.Fbg_vs_stol = Fbg_water SIM.amorphous_sample_thick_mm = 0.1 SIM.amorphous_density_gcm3 = 1 SIM.amorphous_molecular_weight_Da = 18 SIM.add_background() # add air contribution SIM.Fbg_vs_stol = Fbg_air SIM.amorphous_sample_thick_mm = 35 # between beamstop and collimator SIM.amorphous_density_gcm3 = 1.2e-3 SIM.amorphous_sample_molecular_weight_Da = 28 # nitrogen = N2 SIM.add_background() # add nanocrystalline cubic ice contribution (unscaled) SIM.Fbg_vs_stol = Fbg_nanoice SIM.amorphous_sample_thick_mm = 0.05 # between beamstop and collimator SIM.amorphous_density_gcm3 = 0.95 SIM.amorphous_sample_molecular_weight_Da = 18 # H2O SIM.add_background() # # set this to 0 or -1 to trigger automatic radius. could be very slow with bright images SIM.detector_psf_kernel_radius_pixels = 5 # turn off the point spread function for Pilatus SIM.detector_psf_fwhm_mm = 0.0 SIM.detector_psf_type = shapetype.Gauss SIM.adc_offset_adu = 0 SIM.readout_noise_adu = 0 dim = SIM.detpixels_fastslow SIM.region_of_interest = ((0, dim[0]), (0, dim[1])) SIM.add_noise() return SIM
def __init__(self, obj, beam): from dxtbx.model import Detector, Panel from cctbx.eltbx import attenuation_coefficient from dxtbx.model import ParallaxCorrectedPxMmStrategy from scitbx import matrix # Get the handles nx_file = obj.handle.file nx_detector = obj.handle nx_module = obj.modules[0].handle # Get the detector name and type detector_type = str(nx_detector['type'][()]) detector_name = str(nx_detector.name) # Get the trusted range of pixel values trusted_range = (-1, float(nx_detector['saturation_value'][()])) # Get the detector thickness thickness = nx_detector['sensor_thickness'] thickness_value = float(thickness[()]) thickness_units = thickness.attrs['units'] thickness_value = float(convert_units( thickness_value, thickness_units, "mm")) # Get the detector material material = str(nx_detector['sensor_material'][()]) # Get the fast pixel size and vector fast_pixel_direction = nx_module['fast_pixel_direction'] fast_pixel_direction_value = float(fast_pixel_direction[()]) fast_pixel_direction_units = fast_pixel_direction.attrs['units'] fast_pixel_direction_vector = fast_pixel_direction.attrs['vector'] fast_pixel_direction_value = convert_units( fast_pixel_direction_value, fast_pixel_direction_units, "mm") fast_axis = matrix.col(fast_pixel_direction_vector).normalize() # Get the slow pixel size and vector slow_pixel_direction = nx_module['slow_pixel_direction'] slow_pixel_direction_value = float(slow_pixel_direction[()]) slow_pixel_direction_units = slow_pixel_direction.attrs['units'] slow_pixel_direction_vector = slow_pixel_direction.attrs['vector'] slow_pixel_direction_value = convert_units( slow_pixel_direction_value, slow_pixel_direction_units, "mm") slow_axis = matrix.col(slow_pixel_direction_vector).normalize() # Get the origin vector module_offset = nx_module['module_offset'] origin = construct_vector( nx_file, module_offset.name) # Ensure that fast and slow axis are orthogonal normal = fast_axis.cross(slow_axis) slow_axis = -fast_axis.cross(normal) # Compute the attenuation coefficient. # This will fail for undefined composite materials # mu_at_angstrom returns cm^-1, but need mu in mm^-1 if material == 'Si': pass elif material == 'Silicon': material = 'Si' elif material == 'Sillicon': material = 'Si' elif material == 'CdTe': pass elif material == 'GaAs': pass else: raise RuntimeError('Unknown material: %s' % material) table = attenuation_coefficient.get_table(material) wavelength = beam.get_wavelength() mu = table.mu_at_angstrom(wavelength) / 10.0 # Construct the detector model pixel_size = (fast_pixel_direction_value, slow_pixel_direction_value) image_size = tuple(map(int, nx_module['data_size'])) self.model = Detector() self.model.add_panel( Panel( detector_type, detector_name, tuple(fast_axis), tuple(slow_axis), tuple(origin), pixel_size, image_size, trusted_range, thickness_value, material, mu)) # Set the parallax correction for panel in self.model: panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, thickness_value)) panel.set_type('SENSOR_PAD')
def XDS_INP(self, out=None, space_group_number=None, real_space_a=None, real_space_b=None, real_space_c=None, job_card="XYCORR INIT COLSPOT IDXREF DEFPIX INTEGRATE CORRECT"): if out is None: out = sys.stdout assert [real_space_a, real_space_b, real_space_c].count(None) in (0,3) sensor = self.get_detector()[0].get_type() fast, slow = self.detector_size f, s = self.pixel_size df = int(1000 * f) ds = int(1000 * s) # FIXME probably need to rotate by pi about the X axis detector = xds_detector_name( detector_helpers_types.get(sensor, fast, slow, df, ds)) trusted = self.get_detector()[0].get_trusted_range() print >> out, 'DETECTOR=%s MINIMUM_VALID_PIXEL_VALUE=%d OVERLOAD=%d' % \ (detector, trusted[0] + 1, trusted[1]) if detector == 'PILATUS': print >> out, 'SENSOR_THICKNESS= %.3f' % \ self.get_detector()[0].get_thickness() if self.get_detector()[0].get_material(): from cctbx.eltbx import attenuation_coefficient material = self.get_detector()[0].get_material() table = attenuation_coefficient.get_table(material) mu = table.mu_at_angstrom(self.wavelength) / 10.0 print >> out, '!SENSOR_MATERIAL / THICKNESS %s %.3f' % \ (material, self.get_detector()[0].get_thickness()) print >> out, '!SILICON= %f' % mu print >> out, 'DIRECTION_OF_DETECTOR_X-AXIS= %.3f %.3f %.3f' % \ self.detector_x_axis print >> out, 'DIRECTION_OF_DETECTOR_Y-AXIS= %.3f %.3f %.3f' % \ self.detector_y_axis print >> out, 'NX=%d NY=%d QX=%.4f QY=%.4f' % (fast, slow, f, s) print >> out, 'DETECTOR_DISTANCE= %.3f' % self.detector_distance print >> out, 'ORGX= %.1f ORGY= %.1f' % self.detector_origin print >> out, 'ROTATION_AXIS= %.3f %.3f %.3f' % \ self.rotation_axis print >> out, 'STARTING_ANGLE= %.3f' % \ self.starting_angle print >> out, 'OSCILLATION_RANGE= %.3f' % \ self.oscillation_range print >> out, 'X-RAY_WAVELENGTH= %.5f' % \ self.wavelength print >> out, 'INCIDENT_BEAM_DIRECTION= %.3f %.3f %.3f' % \ self.beam_vector # FIXME LATER if hasattr(self.get_beam(), "get_polarization_fraction"): print >> out, 'FRACTION_OF_POLARIZATION= %.3f' % \ self.get_beam().get_polarization_fraction() print >> out, 'POLARIZATION_PLANE_NORMAL= %.3f %.3f %.3f' % \ self.get_beam().get_polarization_normal() print >> out, 'NAME_TEMPLATE_OF_DATA_FRAMES= %s' % \ self.get_template().replace('#', '?') print >> out, 'TRUSTED_REGION= 0.0 1.41' for f0, s0, f1, s1 in self.get_detector()[0].get_mask(): print >> out, 'UNTRUSTED_RECTANGLE= %d %d %d %d' % \ (f0 - 1, f1 + 1, s0 - 1, s1 + 1) start_end = self.get_scan().get_image_range() if start_end[0] == 0: start_end = (1, start_end[1]) print >> out, 'DATA_RANGE= %d %d' % start_end print >> out, 'JOB=%s' %job_card if space_group_number is not None: print >> out, 'SPACE_GROUP_NUMBER= %i' %space_group_number if [real_space_a, real_space_b, real_space_c].count(None) == 0: R = self.imagecif_to_xds_transformation_matrix unit_cell_a_axis = R * matrix.col(real_space_a) unit_cell_b_axis = R * matrix.col(real_space_b) unit_cell_c_axis = R * matrix.col(real_space_c) print >> out, "UNIT_CELL_A-AXIS= %.6f %.6f %.6f" %unit_cell_a_axis.elems print >> out, "UNIT_CELL_B-AXIS= %.6f %.6f %.6f" %unit_cell_b_axis.elems print >> out, "UNIT_CELL_C-AXIS= %.6f %.6f %.6f" %unit_cell_c_axis.elems
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): """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 single_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, s0, f1, s1 in determine_pilatus_mask(detector): detector[0].add_mask(f0, s0, f1, s1) detector[0].set_thickness(thickness) detector[0].set_material("Si") detector[0].set_mu(table.mu_at_angstrom(wavelength)) return detector # got to here means 60-panel version from dxtbx.model.detector import HierarchicalDetector from scitbx import matrix d = HierarchicalDetector() 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 = d.add_panel() 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_local_frame(fast.elems, slow.elems, origin_panel.elems) self.coords[panel_name] = (xmin, ymin, xmax, ymax) root.add_panel(p) 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) 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
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 (ie all except CdTe) 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(table.mu_at_angstrom(wavelength)) panel.set_px_mm_strategy(ParallaxCorrectedPxMmStrategy(mu, thickness)) return detector