예제 #1
0
def add_resolution_to_reflections(reflections, datablock):
    '''Add column d to reflection list'''

    # will assume everything from the first detector at the moment - clearly this
    # could be incorrect, will have to do something a little smarter, later

    from dials.algorithms.indexing.indexer import indexer_base
    imageset = datablock.extract_imagesets()[0]

    if 'imageset_id' not in reflections:
        reflections['imageset_id'] = reflections['id']

    spots_mm = indexer_base.map_spots_pixel_to_mm_rad(
        spots=reflections,
        detector=imageset.get_detector(),
        scan=imageset.get_scan())

    indexer_base.map_centroids_to_reciprocal_space(
        spots_mm,
        detector=imageset.get_detector(),
        beam=imageset.get_beam(),
        goniometer=imageset.get_goniometer())

    d_spacings = 1 / spots_mm['rlp'].norms()

    reflections['d'] = d_spacings
예제 #2
0
  def run(self):
    ''' Parse the options. '''
    from dials.util.options import flatten_experiments, flatten_reflections
    # Parse the command line arguments
    params, options = self.parser.parse_args(show_diff_phil=True)
    self.params = params
    experiments = flatten_experiments(params.input.experiments)
    reflections = flatten_reflections(params.input.reflections)

    assert len(reflections) == len(experiments) == 1
    reflections = reflections[0]
    exp = experiments[0]

    from dials.algorithms.indexing import index_reflections
    from dials.algorithms.indexing.indexer import indexer_base

    reflections['id'] = flex.int(len(reflections), -1)
    reflections['imageset_id'] = flex.int(len(reflections), 0)
    reflections = indexer_base.map_spots_pixel_to_mm_rad(reflections, exp.detector, exp.scan)

    indexer_base.map_centroids_to_reciprocal_space(
      reflections, exp.detector, exp.beam, exp.goniometer,)

    index_reflections(reflections,
                      experiments, params.d_min,
                      tolerance=0.3)
    indexed_reflections = reflections.select(reflections['miller_index'] != (0,0,0))
    print "Indexed %d reflections out of %d"%(len(indexed_reflections), len(reflections))
    easy_pickle.dump("indexedstrong.pickle", indexed_reflections)
  def get_origin_offset_score(self, trial_origin_offset, solutions, amax, spots_mm, imageset):
    from rstbx.indexing_api import lattice # import dependency
    from rstbx.indexing_api import dps_extended
    trial_detector = dps_extended.get_new_detector(imageset.get_detector(), trial_origin_offset)

    from dials.algorithms.indexing.indexer import indexer_base
    indexer_base.map_centroids_to_reciprocal_space(
      spots_mm, trial_detector, imageset.get_beam(), imageset.get_goniometer())

    return self.sum_score_detail(spots_mm['rlp'], solutions, amax=amax)
  def get_origin_offset_score(self, trial_origin_offset, solutions, amax, spots_mm, imageset):
    from rstbx.indexing_api import lattice # import dependency
    from rstbx.indexing_api import dps_extended
    trial_detector = dps_extended.get_new_detector(imageset.get_detector(), trial_origin_offset)

    from dials.algorithms.indexing.indexer import indexer_base
    # Key point for this is that the spots must correspond to detector
    # positions not to the correct RS position => reset any fixed rotation
    # to identity - copy in case called from elsewhere
    import copy
    gonio = copy.deepcopy(imageset.get_goniometer())
    gonio.set_fixed_rotation((1, 0, 0, 0, 1, 0, 0, 0, 1))
    indexer_base.map_centroids_to_reciprocal_space(
      spots_mm, trial_detector, imageset.get_beam(), gonio)

    return self.sum_score_detail(spots_mm['rlp'], solutions, amax=amax)
  def get_origin_offset_score(self, trial_origin_offset, solutions, amax, spots_mm, imageset):
    from rstbx.indexing_api import lattice # import dependency
    from rstbx.indexing_api import dps_extended
    trial_detector = dps_extended.get_new_detector(imageset.get_detector(), trial_origin_offset)

    from dials.algorithms.indexing.indexer import indexer_base
    # Key point for this is that the spots must correspond to detector
    # positions not to the correct RS position => reset any fixed rotation
    # to identity - copy in case called from elsewhere
    import copy
    gonio = copy.deepcopy(imageset.get_goniometer())
    gonio.set_fixed_rotation((1, 0, 0, 0, 1, 0, 0, 0, 1))
    indexer_base.map_centroids_to_reciprocal_space(
      spots_mm, trial_detector, imageset.get_beam(), gonio)

    return self.sum_score_detail(spots_mm['rlp'], solutions, amax=amax)
예제 #6
0
def discover_better_experimental_model(imagesets,
                                       spot_lists,
                                       params,
                                       dps_params,
                                       nproc=1,
                                       wide_search_binning=1):
    assert len(imagesets) == len(spot_lists)
    assert len(imagesets) > 0
    # XXX should check that all the detector and beam objects are the same
    from dials.algorithms.indexing.indexer import indexer_base
    spot_lists_mm = [
        indexer_base.map_spots_pixel_to_mm_rad(spots, imageset.get_detector(),
                                               imageset.get_scan())
        for spots, imageset in zip(spot_lists, imagesets)
    ]

    spot_lists_mm = []
    max_cell_list = []

    detector = imagesets[0].get_detector()
    beam = imagesets[0].get_beam()

    beam_panel = detector.get_panel_intersection(beam.get_s0())

    if beam_panel == -1:
        from libtbx.utils import Sorry
        raise Sorry('input beam does not intersect detector')

    for imageset, spots in zip(imagesets, spot_lists):
        if 'imageset_id' not in spots:
            spots['imageset_id'] = spots['id']

        spots_mm = indexer_base.map_spots_pixel_to_mm_rad(
            spots=spots,
            detector=imageset.get_detector(),
            scan=imageset.get_scan())

        indexer_base.map_centroids_to_reciprocal_space(
            spots_mm,
            detector=imageset.get_detector(),
            beam=imageset.get_beam(),
            goniometer=imageset.get_goniometer())

        if dps_params.d_min is not None:
            d_spacings = 1 / spots_mm['rlp'].norms()
            sel = d_spacings > dps_params.d_min
            spots_mm = spots_mm.select(sel)

        # derive a max_cell from mm spots

        if params.max_cell is None:
            from dials.algorithms.indexing.indexer import find_max_cell
            max_cell = find_max_cell(spots_mm,
                                     max_cell_multiplier=1.3,
                                     step_size=45).max_cell
            max_cell_list.append(max_cell)

        if (params.max_reflections is not None
                and spots_mm.size() > params.max_reflections):
            logger.info('Selecting subset of %i reflections for analysis' %
                        params.max_reflections)
            perm = flex.random_permutation(spots_mm.size())
            sel = perm[:params.max_reflections]
            spots_mm = spots_mm.select(sel)

        spot_lists_mm.append(spots_mm)

    if params.max_cell is None:
        max_cell = flex.median(flex.double(max_cell_list))
    else:
        max_cell = params.max_cell
    args = [(imageset, spots, max_cell, dps_params)
            for imageset, spots in zip(imagesets, spot_lists_mm)]

    from libtbx import easy_mp
    results = easy_mp.parallel_map(func=run_dps,
                                   iterable=args,
                                   processes=nproc,
                                   method="multiprocessing",
                                   preserve_order=True,
                                   asynchronous=True,
                                   preserve_exception_message=True)
    solution_lists = [r["solutions"] for r in results]
    amax_list = [r["amax"] for r in results]
    assert len(solution_lists) > 0

    detector = imagesets[0].get_detector()
    beam = imagesets[0].get_beam()

    # perform calculation
    if dps_params.indexing.improve_local_scope == "origin_offset":
        discoverer = better_experimental_model_discovery(
            imagesets,
            spot_lists_mm,
            solution_lists,
            amax_list,
            dps_params,
            wide_search_binning=wide_search_binning)
        new_detector = discoverer.optimize_origin_offset_local_scope()
        old_panel, old_beam_centre = detector.get_ray_intersection(
            beam.get_s0())
        new_panel, new_beam_centre = new_detector.get_ray_intersection(
            beam.get_s0())

        old_beam_centre_px = detector[old_panel].millimeter_to_pixel(
            old_beam_centre)
        new_beam_centre_px = new_detector[new_panel].millimeter_to_pixel(
            new_beam_centre)

        logger.info("Old beam centre: %.2f, %.2f mm" % old_beam_centre +
                    " (%.1f, %.1f px)" % old_beam_centre_px)
        logger.info("New beam centre: %.2f, %.2f mm" % new_beam_centre +
                    " (%.1f, %.1f px)" % new_beam_centre_px)
        logger.info(
            "Shift: %.2f, %.2f mm" %
            (matrix.col(old_beam_centre) - matrix.col(new_beam_centre)).elems +
            " (%.1f, %.1f px)" % (matrix.col(old_beam_centre_px) -
                                  matrix.col(new_beam_centre_px)).elems)
        return new_detector, beam
    elif dps_params.indexing.improve_local_scope == "S0_vector":
        raise NotImplementedError()
예제 #7
0
파일: rl_csv.py 프로젝트: cctbx-xfel/dials
def run(args):
    import libtbx.load_env
    from dials.util import log
    usage = "%s [options] datablock.json strong.pickle" % libtbx.env.dispatcher_name

    parser = OptionParser(usage=usage,
                          phil=phil_scope,
                          read_datablocks=True,
                          read_reflections=True,
                          check_format=False,
                          epilog=help_message)

    params, options = parser.parse_args(show_diff_phil=False)
    datablocks = flatten_datablocks(params.input.datablock)
    reflections = flatten_reflections(params.input.reflections)

    if len(datablocks) == 0 or len(reflections) == 0:
        parser.print_help()
        exit(0)

    imagesets = []

    for db in datablocks:
        imagesets.extend(db.extract_imagesets())

    spots = []

    for reflection in reflections:
        unique_ids = set(reflection['id'])
        for unique_id in sorted(unique_ids):
            spots.append(reflection.select(reflection['id'] == unique_id))

    assert len(imagesets) == len(spots)

    if params.output.compress:
        import gzip
        fout = gzip.GzipFile(params.output.csv, 'w')
    else:
        fout = open(params.output.csv, 'w')

    fout.write('# x,y,z,experiment_id,imageset_id\n')

    dp = params.output.dp

    if dp <= 0:
        fmt = '%f,%f,%f,%d,%d\n'
    else:
        fmt = '%%.%df,%%.%df,%%.%df,%%d,%%d\n' % (dp, dp, dp)

    print 'Using format:', fmt.strip()

    for k, (imageset, refl) in enumerate(zip(imagesets, spots)):
        if 'imageset_id' not in refl:
            refl['imageset_id'] = refl['id']

        reflmm = indexer_base.map_spots_pixel_to_mm_rad(
            spots=refl,
            detector=imageset.get_detector(),
            scan=imageset.get_scan())

        indexer_base.map_centroids_to_reciprocal_space(
            reflmm,
            detector=imageset.get_detector(),
            beam=imageset.get_beam(),
            goniometer=imageset.get_goniometer())

        rlp = reflmm['rlp']

        for _rlp in rlp:
            fout.write(fmt % (_rlp[0], _rlp[1], _rlp[2], k, k))

        print 'Appended %d spots to %s' % (len(rlp), params.output.csv)

    fout.close()
예제 #8
0
e = Experiment()
e.beam = beamA
e.crystal = optCrystal
e.detector = detector
e2 = deepcopy(e)
e2.beam = beamB
e2.crystal = e.crystal
e3 = deepcopy(e)
beamAB = deepcopy(beamA)
waveAB = beamA.get_wavelength()*.5 + beamB.get_wavelength()*.5
beamAB.set_wavelength( waveAB)
e3.beam = beamAB
e3.crystal = e.crystal
el = ExperimentList()
el.append(e)
el.append(e2)
el.append(e3)
####

# make the rlps
refls_w_mm = indexer_base.map_spots_pixel_to_mm_rad(refls, detector, scan=None)
indexer_base.map_centroids_to_reciprocal_space(refls_w_mm, detector, beamA, goniometer=None)
rlps1 = refls_w_mm['rlp']
indexer_base.map_centroids_to_reciprocal_space(refls_w_mm, detector, beamB, goniometer=None)
rlps2 = refls_w_mm['rlp']

for i in range(len(refls_w_mm)):
    refls_w_mm['id'][i] = -1
spot_utils.as_single_shot_reflections(refls_w_mm)
index_reflections_detail(None, el, refls_w_mm, detector, rlps1, rlps2)
예제 #9
0
def run(space_group_info):
    datablock_json = os.path.join(dials_regression, "indexing_test_data",
                                  "i04_weak_data", "datablock_orig.json")

    datablock = load.datablock(datablock_json, check_format=False)[0]
    sweep = datablock.extract_imagesets()[0]

    sweep._indices = sweep._indices[:20]
    sweep.set_scan(sweep.get_scan()[:20])

    import random
    space_group = space_group_info.group()
    unit_cell = space_group_info.any_compatible_unit_cell(
        volume=random.uniform(1e4, 1e6))

    crystal_symmetry = crystal.symmetry(unit_cell=unit_cell,
                                        space_group=space_group)
    crystal_symmetry.show_summary()

    # the reciprocal matrix
    B = matrix.sqr(unit_cell.fractionalization_matrix()).transpose()
    U = random_rotation()
    A = U * B

    direct_matrix = A.inverse()
    cryst_model = Crystal(direct_matrix[0:3],
                          direct_matrix[3:6],
                          direct_matrix[6:9],
                          space_group=space_group)
    experiment = Experiment(imageset=sweep,
                            beam=sweep.get_beam(),
                            detector=sweep.get_detector(),
                            goniometer=sweep.get_goniometer(),
                            scan=sweep.get_scan(),
                            crystal=cryst_model)
    predicted_reflections = flex.reflection_table.from_predictions(experiment)
    use_fraction = 0.3
    use_sel = flex.random_selection(
        len(predicted_reflections),
        int(use_fraction * len(predicted_reflections)))
    predicted_reflections = predicted_reflections.select(use_sel)
    miller_indices = predicted_reflections['miller_index']
    miller_set = miller.set(crystal_symmetry,
                            miller_indices,
                            anomalous_flag=True)
    predicted_reflections['xyzobs.mm.value'] = predicted_reflections[
        'xyzcal.mm']
    predicted_reflections['id'] = flex.int(len(predicted_reflections), 0)
    from dials.algorithms.indexing.indexer import indexer_base
    indexer_base.map_centroids_to_reciprocal_space(predicted_reflections,
                                                   sweep.get_detector(),
                                                   sweep.get_beam(),
                                                   sweep.get_goniometer())

    # check that local and global indexing worked equally well in absence of errors
    result = compare_global_local(experiment, predicted_reflections,
                                  miller_indices)
    assert result.misindexed_local == 0
    assert result.misindexed_global == 0

    a, b, c = map(matrix.col, cryst_model.get_real_space_vectors())
    relative_error = 0.02
    a *= (1 + relative_error)
    b *= (1 + relative_error)
    c *= (1 + relative_error)

    cryst_model2 = Crystal(a, b, c, space_group=space_group)
    experiment.crystal = cryst_model2

    result = compare_global_local(experiment, predicted_reflections,
                                  miller_indices)

    # check that the local indexing did a better job given the errors in the basis vectors
    #assert result.misindexed_local < result.misindexed_global
    assert result.misindexed_local == 0
    assert result.correct_local > result.correct_global
    # usually the number misindexed is much smaller than this
    assert result.misindexed_local < (0.001 * len(result.reflections_local))

    # the reciprocal matrix
    A = matrix.sqr(cryst_model.get_A())
    A = random_rotation(angle_max=0.03) * A

    direct_matrix = A.inverse()
    cryst_model2 = Crystal(direct_matrix[0:3],
                           direct_matrix[3:6],
                           direct_matrix[6:9],
                           space_group=space_group)
    experiment.crystal = cryst_model2

    result = compare_global_local(experiment, predicted_reflections,
                                  miller_indices)

    # check that the local indexing did a better job given the errors in the basis vectors
    assert result.misindexed_local <= result.misindexed_global, (
        result.misindexed_local, result.misindexed_global)
    assert result.misindexed_local < 0.01 * result.correct_local
    assert result.correct_local > result.correct_global
    # usually the number misindexed is much smaller than this
    assert result.misindexed_local < (0.001 * len(result.reflections_local))
예제 #10
0
def run(space_group_info):
  datablock_json = os.path.join(
    dials_regression, "indexing_test_data",
    "i04_weak_data", "datablock_orig.json")

  datablock = load.datablock(datablock_json, check_format=False)[0]
  sweep = datablock.extract_imagesets()[0]

  sweep._indices = sweep._indices[:20]
  sweep.set_scan(sweep.get_scan()[:20])

  import random
  space_group = space_group_info.group()
  unit_cell = space_group_info.any_compatible_unit_cell(volume=random.uniform(1e4,1e6))

  crystal_symmetry = crystal.symmetry(unit_cell=unit_cell,
                                      space_group=space_group)
  crystal_symmetry.show_summary()

  # the reciprocal matrix
  B = matrix.sqr(unit_cell.fractionalization_matrix()).transpose()
  U = random_rotation()
  A = U * B

  direct_matrix = A.inverse()
  cryst_model = crystal_model(direct_matrix[0:3],
                              direct_matrix[3:6],
                              direct_matrix[6:9],
                              space_group=space_group)
  experiment = Experiment(imageset=sweep,
                          beam=sweep.get_beam(),
                          detector=sweep.get_detector(),
                          goniometer=sweep.get_goniometer(),
                          scan=sweep.get_scan(),
                          crystal=cryst_model)
  predicted_reflections = flex.reflection_table.from_predictions(
    experiment)
  use_fraction = 0.3
  use_sel = flex.random_selection(
    len(predicted_reflections), int(use_fraction*len(predicted_reflections)))
  predicted_reflections = predicted_reflections.select(use_sel)
  miller_indices = predicted_reflections['miller_index']
  miller_set = miller.set(
    crystal_symmetry, miller_indices, anomalous_flag=True)
  predicted_reflections['xyzobs.mm.value'] = predicted_reflections['xyzcal.mm']
  predicted_reflections['id'] = flex.int(len(predicted_reflections), 0)
  from dials.algorithms.indexing.indexer import indexer_base
  indexer_base.map_centroids_to_reciprocal_space(
    predicted_reflections, sweep.get_detector(), sweep.get_beam(),
    sweep.get_goniometer())


  # check that local and global indexing worked equally well in absence of errors
  result = compare_global_local(experiment, predicted_reflections,
                                miller_indices)
  assert result.misindexed_local == 0
  assert result.misindexed_global == 0

  a, b, c = cryst_model.get_real_space_vectors()
  relative_error = 0.02
  a *= (1+relative_error)
  b *= (1+relative_error)
  c *= (1+relative_error)

  cryst_model2 = crystal_model(a, b, c, space_group=space_group)
  experiment.crystal = cryst_model2

  result = compare_global_local(experiment, predicted_reflections,
                                miller_indices)

  # check that the local indexing did a better job given the errors in the basis vectors
  #assert result.misindexed_local < result.misindexed_global
  assert result.misindexed_local == 0
  assert result.correct_local > result.correct_global
  # usually the number misindexed is much smaller than this
  assert result.misindexed_local < (0.001 * len(result.reflections_local))

  # the reciprocal matrix
  A = cryst_model.get_A()
  A = random_rotation(angle_max=0.03) * A

  direct_matrix = A.inverse()
  cryst_model2 = crystal_model(direct_matrix[0:3],
                               direct_matrix[3:6],
                               direct_matrix[6:9],
                               space_group=space_group)
  experiment.crystal = cryst_model2

  result = compare_global_local(experiment, predicted_reflections,
                                miller_indices)

  # check that the local indexing did a better job given the errors in the basis vectors
  assert result.misindexed_local <= result.misindexed_global, (
    result.misindexed_local, result.misindexed_global)
  assert result.misindexed_local < 0.01 * result.correct_local
  assert result.correct_local > result.correct_global
  # usually the number misindexed is much smaller than this
  assert result.misindexed_local < (0.001 * len(result.reflections_local))
예제 #11
0
def run(args):

    from dials.util.options import OptionParser
    from dials.util.options import flatten_datablocks
    from dials.util.options import flatten_experiments
    from dials.util.options import flatten_reflections
    from dials.util import log
    import libtbx.load_env

    usage = "%s [options] datablock.json reflections.pickle" % (
        libtbx.env.dispatcher_name)

    parser = OptionParser(usage=usage,
                          phil=phil_scope,
                          read_datablocks=True,
                          read_experiments=True,
                          read_reflections=True,
                          check_format=False,
                          epilog=help_message)

    params, options = parser.parse_args()
    datablocks = flatten_datablocks(params.input.datablock)
    experiments = flatten_experiments(params.input.experiments)
    reflections = flatten_reflections(params.input.reflections)

    if (len(datablocks) == 0
            and len(experiments) == 0) or len(reflections) == 0:
        parser.print_help()
        exit(0)

    # Configure the logging
    log.config(info='dials.rl_png.log')

    # Log the diff phil
    diff_phil = parser.diff_phil.as_str()
    if diff_phil is not '':
        logger.info('The following parameters have been modified:\n')
        logger.info(diff_phil)

    reflections = reflections[0]

    if len(datablocks) == 0 and len(experiments) > 0:
        imagesets = experiments.imagesets()
    else:
        imagesets = []
        for datablock in datablocks:
            imagesets.extend(datablock.extract_imagesets())

    f = ReciprocalLatticePng(settings=params)
    f.load_models(imagesets, reflections, None)

    imageset = imagesets[0]
    rotation_axis = matrix.col(imageset.get_goniometer().get_rotation_axis())
    s0 = matrix.col(imageset.get_beam().get_s0())

    e1 = rotation_axis.normalize()
    e2 = s0.normalize()
    e3 = e1.cross(e2).normalize()
    #print e1
    #print e2
    #print e3

    f.viewer.plot('rl_rotation_axis.png', n=e1.elems)
    f.viewer.plot('rl_beam_vector', n=e2.elems)
    f.viewer.plot('rl_e3.png', n=e3.elems)

    n_solutions = params.basis_vector_search.n_solutions

    if len(experiments):
        for i, c in enumerate(experiments.crystals()):
            A = matrix.sqr(c.get_A())
            astar = A[:3]
            bstar = A[3:6]
            cstar = A[6:9]

            direct_matrix = A.inverse()
            a = direct_matrix[:3]
            b = direct_matrix[3:6]
            c = direct_matrix[6:9]

            prefix = ''
            if len(experiments.crystals()) > 1:
                prefix = '%i_' % (i + 1)

            f.viewer.plot('rl_%sa.png' % prefix, n=a)
            f.viewer.plot('rl_%sb.png' % prefix, n=b)
            f.viewer.plot('rl_%sc.png' % prefix, n=c)

    elif n_solutions:
        from dials.command_line.search_beam_position \
             import run_dps, dps_phil_scope

        hardcoded_phil = dps_phil_scope.extract()
        hardcoded_phil.d_min = params.d_min

        imageset = imagesets[0]
        from dials.algorithms.indexing.indexer import indexer_base

        if 'imageset_id' not in reflections:
            reflections['imageset_id'] = reflections['id']

        spots_mm = indexer_base.map_spots_pixel_to_mm_rad(
            spots=reflections,
            detector=imageset.get_detector(),
            scan=imageset.get_scan())

        indexer_base.map_centroids_to_reciprocal_space(
            spots_mm,
            detector=imageset.get_detector(),
            beam=imageset.get_beam(),
            goniometer=imageset.get_goniometer())

        if params.d_min is not None:
            d_spacings = 1 / spots_mm['rlp'].norms()
            sel = d_spacings > params.d_min
            spots_mm = spots_mm.select(sel)

        # derive a max_cell from mm spots

        from dials.algorithms.indexing.indexer import find_max_cell
        max_cell = find_max_cell(spots_mm,
                                 max_cell_multiplier=1.3,
                                 step_size=45).max_cell

        result = run_dps((imageset, spots_mm, max_cell, hardcoded_phil))
        solutions = [matrix.col(v) for v in result['solutions']]
        for i in range(min(n_solutions, len(solutions))):
            v = solutions[i]
            #if i > 0:
            #for v1 in solutions[:i-1]:
            #angle = v.angle(v1, deg=True)
            #print angle
            f.viewer.plot('rl_solution_%s.png' % (i + 1), n=v.elems)
def discover_better_experimental_model(
  imagesets, spot_lists, params, dps_params, nproc=1, wide_search_binning=1):
  assert len(imagesets) == len(spot_lists)
  assert len(imagesets) > 0
  # XXX should check that all the detector and beam objects are the same
  from dials.algorithms.indexing.indexer import indexer_base
  spot_lists_mm = [
    indexer_base.map_spots_pixel_to_mm_rad(
      spots, imageset.get_detector(), imageset.get_scan())
    for spots, imageset in zip(spot_lists, imagesets)]

  spot_lists_mm = []
  max_cell_list = []

  detector = imagesets[0].get_detector()
  beam = imagesets[0].get_beam()

  beam_panel = detector.get_panel_intersection(beam.get_s0())

  if beam_panel == -1:
    from libtbx.utils import Sorry
    raise Sorry, 'input beam does not intersect detector'

  for imageset, spots in zip(imagesets, spot_lists):
    if 'imageset_id' not in spots:
      spots['imageset_id'] = spots['id']

    spots_mm = indexer_base.map_spots_pixel_to_mm_rad(
      spots=spots, detector=imageset.get_detector(), scan=imageset.get_scan())

    indexer_base.map_centroids_to_reciprocal_space(
      spots_mm, detector=imageset.get_detector(), beam=imageset.get_beam(),
      goniometer=imageset.get_goniometer())

    if dps_params.d_min is not None:
      d_spacings = 1/spots_mm['rlp'].norms()
      sel = d_spacings > dps_params.d_min
      spots_mm = spots_mm.select(sel)

    # derive a max_cell from mm spots

    if params.max_cell is None:
      from dials.algorithms.indexing.indexer import find_max_cell
      max_cell = find_max_cell(spots_mm, max_cell_multiplier=1.3,
                               step_size=45,
                               nearest_neighbor_percentile=0.05).max_cell
      max_cell_list.append(max_cell)

    if (params.max_reflections is not None and
        spots_mm.size() > params.max_reflections):
      logger.info('Selecting subset of %i reflections for analysis'
           %params.max_reflections)
      perm = flex.random_permutation(spots_mm.size())
      sel = perm[:params.max_reflections]
      spots_mm = spots_mm.select(sel)

    spot_lists_mm.append(spots_mm)

  if params.max_cell is None:
    max_cell = flex.median(flex.double(max_cell_list))
  else:
    max_cell = params.max_cell
  args = [(imageset, spots, max_cell, dps_params)
          for imageset, spots in zip(imagesets, spot_lists_mm)]

  from libtbx import easy_mp
  results = easy_mp.parallel_map(
    func=run_dps,
    iterable=args,
    processes=nproc,
    method="multiprocessing",
    preserve_order=True,
    asynchronous=True,
    preserve_exception_message=True)
  solution_lists = [r["solutions"] for r in results]
  amax_list = [r["amax"] for r in results]
  assert len(solution_lists) > 0

  detector = imagesets[0].get_detector()
  beam = imagesets[0].get_beam()

  # perform calculation
  if dps_params.indexing.improve_local_scope == "origin_offset":
    discoverer = better_experimental_model_discovery(
      imagesets, spot_lists_mm, solution_lists, amax_list, dps_params,
      wide_search_binning=wide_search_binning)
    new_detector = discoverer.optimize_origin_offset_local_scope()
    old_beam_centre = detector.get_ray_intersection(beam.get_s0())[1]
    new_beam_centre = new_detector.get_ray_intersection(beam.get_s0())[1]
    logger.info("Old beam centre: %.2f mm, %.2f mm" %old_beam_centre)
    logger.info("New beam centre: %.2f mm, %.2f mm" %new_beam_centre)
    logger.info("Shift: %.2f mm, %.2f mm" %(
      matrix.col(old_beam_centre)-matrix.col(new_beam_centre)).elems)
    return new_detector, beam
  elif dps_params.indexing.improve_local_scope=="S0_vector":
    raise NotImplementedError()