Esempio n. 1
0
        def get_maxima(self, threshold=5.0):
            '''Run diffdump, printpeaks to get a list of diffraction maxima
      at their image positions, to allow for further analysis.'''

            if not self._image:
                raise RuntimeError('image not set')

            if not os.path.exists(self._image):
                raise RuntimeError('image %s does not exist' % \
                      self._image)

            dd = Diffdump()
            dd.set_image(self._image)
            header = dd.readheader()

            beam = header['raw_beam']
            pixel = header['pixel']

            template, directory = image2template_directory(self._image)
            image_number = image2image(os.path.split(self._image)[-1])

            spot_file = '%s.spt' % template_number2image(
                template, image_number)

            self.start()

            self.input('template "%s"' % template)
            self.input('directory "%s"' % directory)
            self.input('findspots local find %d file %s' % \
                       (image_number, spot_file))
            self.input('go')

            self.close_wait()

            self.check_for_errors()

            output = open(os.path.join(self.get_working_directory(),
                                       spot_file)).readlines()

            os.remove(os.path.join(self.get_working_directory(), spot_file))

            peaks = []

            for record in output[3:-2]:
                lst = record.split()
                x = float(lst[0])
                y = float(lst[1])
                i = float(lst[4]) / float(lst[5])
                x /= pixel[0]
                y /= pixel[1]

                if i < threshold:
                    continue

                # this is Mosflm right? Swap X & Y!!

                peaks.append((y, x, i))

            return peaks
Esempio n. 2
0
def epocher(images):
    '''Get a list of epochs for each image in this list, returning as
  a list of tuples.'''

    result = []
    for i in images:
        dd = Diffdump()
        dd.set_image(i)
        header = dd.readheader()

        e = header['epoch']
        result.append((i, e))

    return result
Esempio n. 3
0
def epocher(images):
  '''Get a list of epochs for each image in this list, returning as
  a list of tuples.'''

  result = []
  for i in images:
    dd = Diffdump()
    dd.set_image(i)
    header = dd.readheader()

    e = header['epoch']
    result.append((i, e))

  return result
Esempio n. 4
0
File: Sweep.py Progetto: hainm/xia2
  def _read_headers(self):
    '''Get the image headers for all of the images - this is not designed
    to be called externally.'''

    self._headers = { }

    t = time.time()

    for i in self._images:
      dd = Diffdump()
      image = self.imagename(i)
      dd.set_image(image)
      header = dd.readheader()
      self._headers[i] = header

    return
Esempio n. 5
0
        def get_maxima(self):
            '''Run diffdump, printpeaks to get a list of diffraction maxima
      at their image positions, to allow for further analysis.'''

            if not self._image:
                raise RuntimeError('image not set')

            if not os.path.exists(self._image):
                raise RuntimeError('image %s does not exist' % \
                      self._image)

            dd = Diffdump()
            dd.set_image(self._image)
            header = dd.readheader()

            beam = header['raw_beam']
            pixel = header['pixel']

            self.add_command_line(self._image)
            self.start()
            self.close_wait()

            self.check_for_errors()

            # results were ok, so get all of the output out
            output = self.get_all_output()

            peaks = []

            for record in output:

                if not 'Peak' in record[:4]:
                    continue

                lst = record.replace(':', ' ').split()
                x = float(lst[4])
                y = float(lst[6])
                i = float(lst[-1])
                x += beam[0]
                y += beam[1]
                x /= pixel[0]
                y /= pixel[1]

                peaks.append((x, y, i))

            return peaks
Esempio n. 6
0
def accumulate(images):
  '''Accumulate dose as a function of image epoch.'''

  dose = { }
  integrated_dose = { }

  for i in images:
    dd = Diffdump()
    dd.set_image(i)
    header = dd.readheader()

    d = header['exposure_time']
    e = header['epoch']

    dose[e] = d

  keys = dose.keys()

  keys.sort()

  accum = 0.0

  for k in keys:
    integrated_dose[k] = accum + 0.5 * dose[k]
    accum += dose[k]

  # ok, now check that the value for the maximum dose is < 1e6 - elsewise
  # chef may explode. or write out ****** as the values are too large for
  # an f8.1 format statement.

  max_dose = max([integrated_dose[k] for k in keys])

  factor = 1.0

  while max_dose / factor > 1.0e6:
    factor *= 10.0

  Debug.write('Doses scaled by %5.2e' % factor)

  # now divide all of those doses by factor

  for k in keys:
    integrated_dose[k] /= factor

  return integrated_dose, factor
Esempio n. 7
0
  def test_i(self):
    idxref = self.Idxref()

    self._index_remove_masked_regions()
    for file in ['SPOT.XDS']:
      idxref.set_input_data_file(file, self._indxr_payload[file])

    idxref.set_data_range(self._indxr_images[0][0],
                          self._indxr_images[0][1])
    idxref.set_background_range(self._indxr_images[0][0],
                                self._indxr_images[0][1])

    # set the phi start etc correctly

    blocks = self._index_select_images_i()

    for block in blocks[:1]:
      starting_frame = block[0]

      dd = Diffdump()
      dd.set_image(self.get_image_name(starting_frame))
      starting_angle = dd.readheader()['phi_start']

      idxref.set_starting_frame(starting_frame)
      idxref.set_starting_angle(starting_angle)

      idxref.add_spot_range(block[0], block[1])

    for block in blocks[1:]:
      idxref.add_spot_range(block[0], block[1])

    #mosflm_beam_centre = self.get_beam_centre()
    #xds_beam_centre = beam_centre_mosflm_to_xds(
        #mosflm_beam_centre[0], mosflm_beam_centre[1], self.get_header())
    from dxtbx.serialize.xds import to_xds
    converter = to_xds(self.get_imageset())
    xds_beam_centre = converter.detector_origin

    idxref.set_beam_centre(xds_beam_centre[0],
                           xds_beam_centre[1])

    idxref.run()

    return idxref.get_fraction_rmsd_rmsphi()
Esempio n. 8
0
def accumulate(images):
    '''Accumulate dose as a function of image epoch.'''

    dose = {}
    integrated_dose = {}

    for i in images:
        dd = Diffdump()
        dd.set_image(i)
        header = dd.readheader()

        d = header['exposure_time']
        e = header['epoch']

        dose[e] = d

    keys = sorted(dose.keys())

    accum = 0.0

    for k in keys:
        integrated_dose[k] = accum + 0.5 * dose[k]
        accum += dose[k]

    # ok, now check that the value for the maximum dose is < 1e6 - elsewise
    # chef may explode. or write out ****** as the values are too large for
    # an f8.1 format statement.

    max_dose = max([integrated_dose[k] for k in keys])

    factor = 1.0

    while max_dose / factor > 1.0e6:
        factor *= 10.0

    Debug.write('Doses scaled by %5.2e' % factor)

    # now divide all of those doses by factor

    for k in keys:
        integrated_dose[k] /= factor

    return integrated_dose, factor
        assert math.fabs((dot(x, d) / math.sqrt(dot(x, x) * dot(d, d))) -
                         1) < 0.001


if __name__ == "__main_work__":
    work_equation_of_line()

if __name__ == "__main__":
    from xia2.Wrappers.XIA.Diffdump import Diffdump

    bm = BackstopMask(sys.argv[1])

    dd = Diffdump()
    dd.set_image(sys.argv[2])
    header = dd.readheader()

    def format_limits(limits):
        assert len(limits) == 4

        result = "limits quad"

        for l in limits:
            result += " %.1f %.1f" % l

        return result

    print(format_limits(bm.calculate_mask_mosflm(header)))

    if len(sys.argv) == 5:
        cbf_in = sys.argv[3]
Esempio n. 10
0
        assert math.fabs((dot(x, d) / math.sqrt(dot(x, x) * dot(d, d))) - 1) < 0.001

    return


if __name__ == "__main_work__":
    work_equation_of_line()

if __name__ == "__main__":
    from xia2.Wrappers.XIA.Diffdump import Diffdump

    bm = BackstopMask(sys.argv[1])

    dd = Diffdump()
    dd.set_image(sys.argv[2])
    header = dd.readheader()

    def format_limits(limits):
        assert len(limits) == 4

        result = "limits quad"

        for l in limits:
            result += " %.1f %.1f" % l

        return result

    print format_limits(bm.calculate_mask_mosflm(header))

    if len(sys.argv) == 5:
        cbf_in = sys.argv[3]
Esempio n. 11
0
def mosflm_check_indexer_solution(indexer):

  distance = indexer.get_indexer_distance()
  axis = matrix.col([0, 0, 1])
  beam = indexer.get_indexer_beam_centre()
  cell = indexer.get_indexer_cell()
  wavelength = indexer.get_wavelength()

  space_group_number = l2s(indexer.get_indexer_lattice())
  spacegroup = sgtbx.space_group_symbols(space_group_number).hall()
  phi = indexer.get_header()['phi_width']

  sg = sgtbx.space_group(spacegroup)

  if not (sg.n_ltr() - 1):
    # primitive solution - just return ... something
    return None, None, None, None

  # FIXME need to raise an exception if this is not available!
  m_matrix = indexer.get_indexer_payload('mosflm_orientation_matrix')

  # N.B. in the calculation below I am using the Cambridge frame
  # and Mosflm definitions of X & Y...

  m_elems = []

  for record in m_matrix[:3]:
    record = record.replace('-', ' -')
    for e in map(float, record.split()):
      m_elems.append(e / wavelength)

  mi = matrix.sqr(m_elems)
  m = mi.inverse()

  A = matrix.col(m.elems[0:3])
  B = matrix.col(m.elems[3:6])
  C = matrix.col(m.elems[6:9])

  # now select the images - start with the images that the indexer
  # used for indexing, though can interrogate the FrameProcessor
  # interface of the indexer to put together a completely different
  # list if I like...

  images = []

  for i in indexer.get_indexer_images():
    for j in i:
      if not j in images:
        images.append(j)

  images.sort()

  # now construct the reciprocal-space peak list n.b. should
  # really run this in parallel...

  spots_r = []

  spots_r_j =  { }

  for i in images:
    image = indexer.get_image_name(i)
    dd = Diffdump()
    dd.set_image(image)
    header = dd.readheader()
    phi = header['phi_start'] + 0.5 * header['phi_width']
    pixel = header['pixel']
    wavelength = header['wavelength']
    peaks = locate_maxima(image)

    spots_r_j[i] = []

    for p in peaks:
      x, y, isigma = p

      if isigma < 5.0:
        continue

      xp = pixel[0] * y - beam[0]
      yp = pixel[1] * x - beam[1]

      scale = wavelength * math.sqrt(
          xp * xp + yp * yp + distance * distance)

      X = distance / scale
      X -= 1.0 / wavelength
      Y = - xp / scale
      Z = yp / scale

      S = matrix.col([X, Y, Z])

      rtod = 180.0 / math.pi

      spots_r.append(S.rotate(axis, - phi / rtod))
      spots_r_j[i].append(S.rotate(axis, - phi / rtod))

  # now reindex the reciprocal space spot list and count - n.b. need
  # to transform the Bravais lattice to an assumed spacegroup and hence
  # to a cctbx spacegroup!

  # lists = [spots_r_j[j] for j in spots_r_j]
  lists = []
  lists.append(spots_r)

  for l in lists:

    absent = 0
    present = 0
    total = 0

    for spot in l:
      hkl = (m * spot).elems

      total += 1

      ihkl = map(nint, hkl)

      if math.fabs(hkl[0] - ihkl[0]) > 0.1:
        continue

      if math.fabs(hkl[1] - ihkl[1]) > 0.1:
        continue

      if math.fabs(hkl[2] - ihkl[2]) > 0.1:
        continue

      # now determine if it is absent

      if sg.is_sys_absent(ihkl):
        absent += 1
      else:
        present += 1

    # now perform the analysis on these numbers...

    sd = math.sqrt(absent)

    if total:

      Debug.write('Counts: %d %d %d %.3f' % \
                  (total, present, absent, (absent - 3 * sd) / total))

    else:

      Debug.write('Not enough spots found for analysis')
      return False, None, None, None

    if (absent - 3 * sd) / total < 0.008:
      return False, None, None, None

  # in here need to calculate the new orientation matrix for the
  # primitive basis and reconfigure the indexer - somehow...

  # ok, so the bases are fine, but what I will want to do is reorder them
  # to give the best primitive choice of unit cell...

  sgp = sg.build_derived_group(True, False)
  lattice_p = s2l(sgp.type().number())
  symm = crystal.symmetry(unit_cell = cell,
                          space_group = sgp)

  rdx = symm.change_of_basis_op_to_best_cell()
  symm_new = symm.change_basis(rdx)

  # now apply this to the reciprocal-space orientation matrix mi

  # cb_op = sgtbx.change_of_basis_op(rdx)
  cb_op = rdx
  R = cb_op.c_inv().r().as_rational().as_float().transpose().inverse()
  mi_r = mi * R

  # now re-derive the cell constants, just to be sure

  m_r = mi_r.inverse()
  Ar = matrix.col(m_r.elems[0:3])
  Br = matrix.col(m_r.elems[3:6])
  Cr = matrix.col(m_r.elems[6:9])

  a = math.sqrt(Ar.dot())
  b = math.sqrt(Br.dot())
  c = math.sqrt(Cr.dot())

  rtod = 180.0 / math.pi

  alpha = rtod * Br.angle(Cr)
  beta = rtod * Cr.angle(Ar)
  gamma = rtod * Ar.angle(Br)

  # print '%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f' % \
  # (a, b, c, alpha, beta, gamma)

  cell = uctbx.unit_cell((a, b, c, alpha, beta, gamma))

  amat = [wavelength * e for e in mi_r.elems]
  bmat = matrix.sqr(cell.fractionalization_matrix())
  umat = mi_r * bmat.inverse()

  # yuk! surely I don't need to do this...

  # I do need to do this, and don't call me shirley!

  new_matrix = ['%s\n' % r for r in \
                format_matrix((a, b, c, alpha, beta, gamma),
                              amat, umat.elems).split('\n')]

  # ok - this gives back the right matrix in the right setting - excellent!
  # now need to apply this back at base to the results of the indexer.

  # N.B. same should be applied to the same calculations for the XDS
  # version of this.

  return True, lattice_p, new_matrix, (a, b, c, alpha, beta, gamma)
def mosflm_check_indexer_solution(indexer):

    distance = indexer.get_indexer_distance()
    axis = matrix.col([0, 0, 1])
    beam = indexer.get_indexer_beam_centre()
    cell = indexer.get_indexer_cell()
    wavelength = indexer.get_wavelength()

    space_group_number = l2s(indexer.get_indexer_lattice())
    spacegroup = sgtbx.space_group_symbols(space_group_number).hall()
    phi = indexer.get_header()['phi_width']

    sg = sgtbx.space_group(spacegroup)

    if not (sg.n_ltr() - 1):
        # primitive solution - just return ... something
        return None, None, None, None

    # FIXME need to raise an exception if this is not available!
    m_matrix = indexer.get_indexer_payload('mosflm_orientation_matrix')

    # N.B. in the calculation below I am using the Cambridge frame
    # and Mosflm definitions of X & Y...

    m_elems = []

    for record in m_matrix[:3]:
        record = record.replace('-', ' -')
        for e in map(float, record.split()):
            m_elems.append(e / wavelength)

    mi = matrix.sqr(m_elems)
    m = mi.inverse()

    A = matrix.col(m.elems[0:3])
    B = matrix.col(m.elems[3:6])
    C = matrix.col(m.elems[6:9])

    # now select the images - start with the images that the indexer
    # used for indexing, though can interrogate the FrameProcessor
    # interface of the indexer to put together a completely different
    # list if I like...

    images = []

    for i in indexer.get_indexer_images():
        for j in i:
            if not j in images:
                images.append(j)

    images.sort()

    # now construct the reciprocal-space peak list n.b. should
    # really run this in parallel...

    spots_r = []

    spots_r_j = {}

    for i in images:
        image = indexer.get_image_name(i)
        dd = Diffdump()
        dd.set_image(image)
        header = dd.readheader()
        phi = header['phi_start'] + 0.5 * header['phi_width']
        pixel = header['pixel']
        wavelength = header['wavelength']
        peaks = locate_maxima(image)

        spots_r_j[i] = []

        for p in peaks:
            x, y, isigma = p

            if isigma < 5.0:
                continue

            xp = pixel[0] * y - beam[0]
            yp = pixel[1] * x - beam[1]

            scale = wavelength * math.sqrt(xp * xp + yp * yp +
                                           distance * distance)

            X = distance / scale
            X -= 1.0 / wavelength
            Y = -xp / scale
            Z = yp / scale

            S = matrix.col([X, Y, Z])

            rtod = 180.0 / math.pi

            spots_r.append(S.rotate(axis, -phi / rtod))
            spots_r_j[i].append(S.rotate(axis, -phi / rtod))

    # now reindex the reciprocal space spot list and count - n.b. need
    # to transform the Bravais lattice to an assumed spacegroup and hence
    # to a cctbx spacegroup!

    # lists = [spots_r_j[j] for j in spots_r_j]
    lists = []
    lists.append(spots_r)

    for l in lists:

        absent = 0
        present = 0
        total = 0

        for spot in l:
            hkl = (m * spot).elems

            total += 1

            ihkl = map(nint, hkl)

            if math.fabs(hkl[0] - ihkl[0]) > 0.1:
                continue

            if math.fabs(hkl[1] - ihkl[1]) > 0.1:
                continue

            if math.fabs(hkl[2] - ihkl[2]) > 0.1:
                continue

            # now determine if it is absent

            if sg.is_sys_absent(ihkl):
                absent += 1
            else:
                present += 1

        # now perform the analysis on these numbers...

        sd = math.sqrt(absent)

        if total:

            Debug.write('Counts: %d %d %d %.3f' % \
                        (total, present, absent, (absent - 3 * sd) / total))

        else:

            Debug.write('Not enough spots found for analysis')
            return False, None, None, None

        if (absent - 3 * sd) / total < 0.008:
            return False, None, None, None

    # in here need to calculate the new orientation matrix for the
    # primitive basis and reconfigure the indexer - somehow...

    # ok, so the bases are fine, but what I will want to do is reorder them
    # to give the best primitive choice of unit cell...

    sgp = sg.build_derived_group(True, False)
    lattice_p = s2l(sgp.type().number())
    symm = crystal.symmetry(unit_cell=cell, space_group=sgp)

    rdx = symm.change_of_basis_op_to_best_cell()
    symm_new = symm.change_basis(rdx)

    # now apply this to the reciprocal-space orientation matrix mi

    # cb_op = sgtbx.change_of_basis_op(rdx)
    cb_op = rdx
    R = cb_op.c_inv().r().as_rational().as_float().transpose().inverse()
    mi_r = mi * R

    # now re-derive the cell constants, just to be sure

    m_r = mi_r.inverse()
    Ar = matrix.col(m_r.elems[0:3])
    Br = matrix.col(m_r.elems[3:6])
    Cr = matrix.col(m_r.elems[6:9])

    a = math.sqrt(Ar.dot())
    b = math.sqrt(Br.dot())
    c = math.sqrt(Cr.dot())

    rtod = 180.0 / math.pi

    alpha = rtod * Br.angle(Cr)
    beta = rtod * Cr.angle(Ar)
    gamma = rtod * Ar.angle(Br)

    # print '%6.3f %6.3f %6.3f %6.3f %6.3f %6.3f' % \
    # (a, b, c, alpha, beta, gamma)

    cell = uctbx.unit_cell((a, b, c, alpha, beta, gamma))

    amat = [wavelength * e for e in mi_r.elems]
    bmat = matrix.sqr(cell.fractionalization_matrix())
    umat = mi_r * bmat.inverse()

    # yuk! surely I don't need to do this...

    # I do need to do this, and don't call me shirley!

    new_matrix = ['%s\n' % r for r in \
                  format_matrix((a, b, c, alpha, beta, gamma),
                                amat, umat.elems).split('\n')]

    # ok - this gives back the right matrix in the right setting - excellent!
    # now need to apply this back at base to the results of the indexer.

    # N.B. same should be applied to the same calculations for the XDS
    # version of this.

    return True, lattice_p, new_matrix, (a, b, c, alpha, beta, gamma)
Esempio n. 13
0
  def _index(self):
    '''Actually do the autoindexing using the data prepared by the
    previous method.'''

    images_str = '%d to %d' % tuple(self._indxr_images[0])
    for i in self._indxr_images[1:]:
      images_str += ', %d to %d' % tuple(i)

    cell_str = None
    if self._indxr_input_cell:
      cell_str = '%.2f %.2f %.2f %.2f %.2f %.2f' % \
                 self._indxr_input_cell

    # then this is a proper autoindexing run - describe this
    # to the journal entry

    #if len(self._fp_directory) <= 50:
      #dirname = self._fp_directory
    #else:
      #dirname = '...%s' % self._fp_directory[-46:]
    dirname = self.get_directory()

    Journal.block('autoindexing', self._indxr_sweep_name, 'XDS',
                  {'images':images_str,
                   'target cell':cell_str,
                   'target lattice':self._indxr_input_lattice,
                   'template':self.get_template(),
                   'directory':dirname})

    idxref = self.Idxref()

    self._index_remove_masked_regions()
    for file in ['SPOT.XDS']:
      idxref.set_input_data_file(file, self._indxr_payload[file])

    idxref.set_data_range(self._indxr_images[0][0],
                          self._indxr_images[0][1])
    idxref.set_background_range(self._indxr_images[0][0],
                                self._indxr_images[0][1])

    # set the phi start etc correctly

    if self._i_or_ii == None:
      self._i_or_ii = self.decide_i_or_ii()
      Debug.write('Selecting I or II, chose %s' % self._i_or_ii)

    if self._i_or_ii == 'i':
      blocks = self._index_select_images_i()
      for block in blocks[:1]:
        starting_frame = block[0]

        dd = Diffdump()
        dd.set_image(self.get_image_name(starting_frame))
        starting_angle = dd.readheader()['phi_start']

        idxref.set_starting_frame(starting_frame)
        idxref.set_starting_angle(starting_angle)

        idxref.add_spot_range(block[0], block[1])

      for block in blocks[1:]:
        idxref.add_spot_range(block[0], block[1])
    else:
      for block in self._indxr_images[:1]:
        starting_frame = block[0]

        dd = Diffdump()
        dd.set_image(self.get_image_name(starting_frame))
        starting_angle = dd.readheader()['phi_start']

        idxref.set_starting_frame(starting_frame)
        idxref.set_starting_angle(starting_angle)

        idxref.add_spot_range(block[0], block[1])

      for block in self._indxr_images[1:]:
        idxref.add_spot_range(block[0], block[1])

    # FIXME need to also be able to pass in the known unit
    # cell and lattice if already available e.g. from
    # the helper... indirectly

    if self._indxr_user_input_lattice:
      idxref.set_indexer_user_input_lattice(True)

    if self._indxr_input_lattice and self._indxr_input_cell:
      idxref.set_indexer_input_lattice(self._indxr_input_lattice)
      idxref.set_indexer_input_cell(self._indxr_input_cell)

      Debug.write('Set lattice: %s' % self._indxr_input_lattice)
      Debug.write('Set cell: %f %f %f %f %f %f' % \
                  self._indxr_input_cell)

      original_cell = self._indxr_input_cell
    elif self._indxr_input_lattice:
      idxref.set_indexer_input_lattice(self._indxr_input_lattice)
      original_cell = None
    else:
      original_cell = None

    # FIXED need to set the beam centre here - this needs to come
    # from the input .xinfo object or header, and be converted
    # to the XDS frame... done.

    #mosflm_beam_centre = self.get_beam_centre()
    #xds_beam_centre = beam_centre_mosflm_to_xds(
        #mosflm_beam_centre[0], mosflm_beam_centre[1], self.get_header())
    from dxtbx.serialize.xds import to_xds
    converter = to_xds(self.get_imageset())
    xds_beam_centre = converter.detector_origin

    idxref.set_beam_centre(xds_beam_centre[0],
                           xds_beam_centre[1])

    # fixme need to check if the lattice, cell have been set already,
    # and if they have, pass these in as input to the indexing job.

    done = False

    while not done:
      try:
        done = idxref.run()

        # N.B. in here if the IDXREF step was being run in the first
        # pass done is FALSE however there should be a refined
        # P1 orientation matrix etc. available - so keep it!

      except XDSException, e:
        # inspect this - if we have complaints about not
        # enough reflections indexed, and we have a target
        # unit cell, and they are the same, well ignore it

        if 'solution is inaccurate' in str(e):
          Debug.write(
              'XDS complains solution inaccurate - ignoring')
          done = idxref.continue_from_error()
        elif ('insufficient percentage (< 70%)' in str(e) or
              'insufficient percentage (< 50%)' in str(e)) and \
                 original_cell:
          done = idxref.continue_from_error()
          lattice, cell, mosaic = \
                   idxref.get_indexing_solution()
          # compare solutions
          for j in range(3):
            # allow two percent variation in unit cell length
            if math.fabs((cell[j] - original_cell[j]) / \
                         original_cell[j]) > 0.02 and \
                         not Flags.get_relax():
              Debug.write('XDS unhappy and solution wrong')
              raise e
            # and two degree difference in angle
            if math.fabs(cell[j + 3] - original_cell[j + 3]) \
                   > 2.0 and not Flags.get_relax():
              Debug.write('XDS unhappy and solution wrong')
              raise e
          Debug.write('XDS unhappy but solution ok')
        elif 'insufficient percentage (< 70%)' in str(e) or \
                 'insufficient percentage (< 50%)' in str(e):
          Debug.write('XDS unhappy but solution probably ok')
          done = idxref.continue_from_error()
        else:
          raise e