def _detector(self):
    '''Return a working detector instance.'''
    if self._panel_origin is not None:
      from dxtbx.model.detector import HierarchicalDetector
      detector = HierarchicalDetector()
      root = detector.hierarchy()
      root.set_frame(self._fast_axis, self._slow_axis, self._detector_origin)

      i_panel = 0
      for p_offset, p_size, origin, fast, slow in zip(
          self._panel_offset, self._panel_size, self._panel_origin,
          self._panel_fast, self._panel_slow):
        # ensure mutual orthogonality in presence of numerical rounding errors
        normal = fast.cross(slow)
        slow = normal.cross(fast)
        p = detector.add_panel()
        root.add_panel(p)
        p.set_type('unknown')
        p.set_raw_image_offset(p_offset)
        p.set_image_size(p_size)
        p.set_name('Panel%d' %i_panel)
        p.set_pixel_size(self._pixel_size)
        p.set_frame(fast.elems, slow.elems, origin.elems)
        i_panel += 1
      return detector

    return self._detector_factory.complex(
        self._detector_factory.sensor('unknown'), self._detector_origin,
        self._fast_axis, self._slow_axis, self._pixel_size,
        self._image_size, (0, 1.e9))
  def _detector(self):
    '''The _detector() function returns a model for a CSPAD detector as
    used at LCLS's CXI and XPP endstations.  It converts the
    metrology information in the pure Python object extracted from
    the image pickle to DXTBX-style transformation vectors.  Only
    ASIC:s are considered, since DXTBX metrology is not concerned
    with hierarchies.

    Merged from xfel.cftbx.detector.cspad_detector.readHeader() and
    xfel.cftbx.detector.metrology.metrology_as_dxtbx_vectors().
    '''

    from dxtbx.model import SimplePxMmStrategy
    from dxtbx.model.detector import HierarchicalDetector
    from scitbx.matrix import col

    # XXX Introduces dependency on cctbx.xfel!  Should probably be
    # merged into the code here!
    from xfel.cftbx.detector.metrology import \
         _transform, get_projection_matrix

    # Apply the detector distance to the translation of the root
    # detector object.
    d = self._metrology_params.detector
    Tb_d = _transform(
      col(d.orientation).normalize(),
      col(d.translation) +
      col((0, 0, -self._metrology_params.distance * 1e-3)))[1]

    self._raw_data = []
    detector = HierarchicalDetector()

    for p in d.panel:
      Tb_p = Tb_d * _transform(
        col(p.orientation).normalize(),
        col(p.translation))[1]

      for s in p.sensor:
        Tb_s = Tb_p * _transform(
          col(s.orientation).normalize(),
          col(s.translation))[1]

        for a in s.asic:
          Tb_a = Tb_s * _transform(
            col(a.orientation).normalize(),
            col(a.translation))[1]

          Pb = get_projection_matrix(a.pixel_size, a.dimension)[1]

          # The DXTBX-style metrology description consists of three
          # vectors for each ASIC.  The origin vector locates the
          # (0, 0)-pixel in the laboratory frame in units of mm.
          # The second and third vectors give the directions to the
          # pixels immediately next to (0, 0) in the fast and slow
          # directions, respectively, in arbitrary units.
          origin = Tb_a * Pb * col((0, 0, 1))
          fast = Tb_a * Pb * col((0, a.dimension[0], 1)) - origin
          slow = Tb_a * Pb * col((a.dimension[1], 0, 1)) - origin

          # Convert vector units from meter to millimeter.  The
          # default, SimplePxMmStrategy applies here.  XXX Due to
          # dark subtraction, a valid pixel intensity may be
          # negative, and this is currently not reflected by
          # trusted_range.
          key = (d.serial, p.serial, s.serial, a.serial)

          panel = detector.add_panel()
          panel.set_type("PAD")
          panel.set_name('%d:%d:%d:%d' % key)
          panel.set_local_frame(
            [t * 1e3 for t in fast.elems[0:3]],
            [t * 1e3 for t in slow.elems[0:3]],
            [t * 1e3 for t in origin.elems[0:3]])

          panel.set_pixel_size([t * 1e3 for t in a.pixel_size])
          panel.set_image_size(a.dimension)
          panel.set_trusted_range((0, a.saturation))

          self._raw_data.append(self._tiles[key])

    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):

    # 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, index=None):
    from dxtbx.model.detector import HierarchicalDetector
    from scitbx import matrix
    import math

    # Ignore this for now.
    # TODO: must consider when a new MPCCD with 300um thickness
    #       is released.

    self.wavelength = 1.77 # A, this must be got from _beam
    #    thickness = 60 # um
    #
    #    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)

    detector = HierarchicalDetector()
    root = detector.hierarchy()
    root.set_frame(
      (1, 0, 0),
      (0, 1, 0),
      (0, 0, - self.distance))

    if self.RECONST_MODE:
      return self._detector_factory.simple(
        sensor = 'PAD',
        distance = self.distance,
        beam_centre = (self.RECONST_SIZE * self.pixel_size / 2,
                       self.RECONST_SIZE * self.pixel_size / 2),
        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, 1000000),
        mask = [])  # a list of dead rectangles

    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 = detector.add_panel()

      # OBS! you need to set the panel to a root before set local frame...
      root.add_panel(p)
      p.set_name('panel-%01d' % i)
      p.set_image_size((512, 1024))
      p.set_trusted_range((-1, 1000000))
      p.set_pixel_size((self.pixel_size, self.pixel_size))
      p.set_local_frame(
        fast.elems,
        slow.elems,
        origin.elems)
      xmin, ymin = 0, i * 1024
      p.set_raw_image_offset((xmin, ymin))
      # p.set_px_mm_strategy(px_mm)

    return detector