Ejemplo n.º 1
0
    def get_top_solutions(self, ai, input_directions, size, cutoff_divisor):
        # size was 30 in the Rossmann DPS paper
        kval_cutoff = ai.getXyzSize() / cutoff_divisor

        hemisphere_solutions = flex.Direction()
        hemisphere_solutions.reserve(size)

        for i in range(len(input_directions)):
            sampled_direction = ai.fft_result(input_directions[i])
            if sampled_direction.kval > kval_cutoff:
                hemisphere_solutions.append(sampled_direction)

        if (hemisphere_solutions.size() < 3):
            return hemisphere_solutions
        kvals = flex.double([
            hemisphere_solutions[x].kval
            for x in range(len(hemisphere_solutions))
        ])

        perm = flex.sort_permutation(kvals, True)

        #  conventional algorithm; just take the top scoring hits.
        #  use this for quick_grid, when it is known ahead of time
        #  that many (hundreds) of directions will be retained

        hemisphere_solutions_sort = flex.Direction(
            [hemisphere_solutions[p] for p in perm[0:min(size, len(perm))]])

        return hemisphere_solutions_sort
Ejemplo n.º 2
0
    def find_basis_vectors(self, reciprocal_lattice_vectors):
        """Find a list of likely basis vectors.

        Args:
            reciprocal_lattice_vectors (scitbx.array_family.flex.vec3_double):
                The list of reciprocal lattice vectors to search for periodicity.
        """
        used_in_indexing = flex.bool(reciprocal_lattice_vectors.size(), True)
        logger.info("Indexing from %i reflections" % used_in_indexing.count(True))

        vectors, weights = self.score_vectors(reciprocal_lattice_vectors)

        perm = flex.sort_permutation(weights, reverse=True)
        vectors = vectors.select(perm)
        weights = weights.select(perm)

        groups = group_vectors(vectors, weights, max_groups=self._params.max_vectors)
        unique_vectors = []
        unique_weights = []
        for g in groups:
            idx = flex.max_index(flex.double(g.weights))
            unique_vectors.append(g.vectors[idx])
            unique_weights.append(g.weights[idx])

        logger.info("Number of unique vectors: %i" % len(unique_vectors))

        for v, w in zip(unique_vectors, unique_weights):
            logger.debug("%s %s %s" % (w, v.length(), str(v.elems)))

        return unique_vectors, used_in_indexing
Ejemplo n.º 3
0
  def get_top_solutions(self,ai,input_directions,size,cutoff_divisor):
    # size was 30 in the Rossmann DPS paper
    kval_cutoff = ai.getXyzSize()/cutoff_divisor;

    hemisphere_solutions = flex.Direction();
    hemisphere_solutions.reserve(size);

    for i in xrange(len(input_directions)):
      sampled_direction = ai.fft_result(input_directions[i])
      if sampled_direction.kval > kval_cutoff:
        hemisphere_solutions.append(sampled_direction)

    if (hemisphere_solutions.size()<3):
      return hemisphere_solutions
    kvals = flex.double([
  hemisphere_solutions[x].kval for x in xrange(len(hemisphere_solutions))])

    perm = flex.sort_permutation(kvals,True)

    #  conventional algorithm; just take the top scoring hits.
    #  use this for quick_grid, when it is known ahead of time
    #  that many (hundreds) of directions will be retained

    hemisphere_solutions_sort = flex.Direction(
        [hemisphere_solutions[p] for p in perm[0:min(size,len(perm))]])

    return hemisphere_solutions_sort;
Ejemplo n.º 4
0
    def restrict_target_angles_to_quick_winners(self, quick_sampling):

        # find which target_grid directions are near neighbors to
        # the quick_sampling winners from the first round of testing.
        # in the second round, restrict the calculation of FFTs to those directions

        self.restricted = flex.Direction()
        target = flex.double()
        for iz in self.angles:
            # fairly inefficient in Python; expect big improvement in C++
            v = iz.dvec
            target.append(v[0])
            target.append(v[1])
            target.append(v[2])
        kn = int(2 * self.second_round_sampling)

        #construct k-d tree for the reference set
        A = AnnAdaptor(data=target, dim=3, k=kn)

        query = flex.double()
        for j in quick_sampling:
            v = j.dvec
            query.append(v[0])
            query.append(v[1])
            query.append(v[2])
            if abs((math.pi / 2.) -
                   j.psi) < 0.0001:  #take care of equatorial boundary
                query.append(-v[0])
                query.append(-v[1])
                query.append(-v[2])

        A.query(query)  #find nearest neighbors of query points
        neighbors = flex.sqrt(A.distances)
        neighborid = A.nn

        accept_flag = flex.bool(len(self.angles))
        for idx in range(len(neighbors)):
            # use small angle approximation to test if target is within desired radius
            if neighbors[idx] < self.quick_grid:
                accept_flag[neighborid[idx]] = True

        #go through all of the original target angles
        for iz in range(len(self.angles)):
            if accept_flag[iz]:
                self.restricted.append(self.angles[iz])

        self.angles = self.restricted
Ejemplo n.º 5
0
  def restrict_target_angles_to_quick_winners(self,quick_sampling):

    # find which target_grid directions are near neighbors to
    # the quick_sampling winners from the first round of testing.
    # in the second round, restrict the calculation of FFTs to those directions

    self.restricted = flex.Direction()
    target = flex.double()
    for iz in self.angles:
      # fairly inefficient in Python; expect big improvement in C++
      v = iz.dvec
      target.append(v[0]);target.append(v[1]);target.append(v[2]);
    kn = int(2 *self.second_round_sampling)

    #construct k-d tree for the reference set
    A = AnnAdaptor(data = target, dim = 3, k = kn)

    query = flex.double()
    for j in quick_sampling:
      v = j.dvec
      query.append(v[0]);query.append(v[1]); query.append(v[2]);
      if abs((math.pi/2.) - j.psi) < 0.0001: #take care of equatorial boundary
        query.append(-v[0]);query.append(-v[1]); query.append(-v[2]);

    A.query(query) #find nearest neighbors of query points
    neighbors = flex.sqrt(A.distances)
    neighborid = A.nn

    accept_flag = flex.bool(len(self.angles))
    for idx in xrange(len(neighbors)):
      # use small angle approximation to test if target is within desired radius
      if neighbors[idx] < self.quick_grid:
        accept_flag[neighborid[idx]] = True

    #go through all of the original target angles
    for iz in xrange(len(self.angles)):
      if accept_flag[iz]:
        self.restricted.append(self.angles[iz])

    self.angles = self.restricted
Ejemplo n.º 6
0
    def get_top_solutions(self, ai, input_directions, size, cutoff_divisor,
                          grid):

        kval_cutoff = ai.getXyzSize() / cutoff_divisor

        hemisphere_solutions = flex.Direction()
        hemisphere_solutions.reserve(size)
        #print "# of input directions", len(input_directions)
        for i in range(len(input_directions)):
            D = sampled_direction = ai.fft_result(input_directions[i])

            #hardcoded parameter in for silicon example.  Not sure at this point how
            #robust the algorithm is when this value is relaxed.

            if D.real < self.max_cell and sampled_direction.kval > kval_cutoff:
                if diagnostic: directional_show(D, "%5d" % i)
                hemisphere_solutions.append(sampled_direction)
        if (hemisphere_solutions.size() < 3):
            return hemisphere_solutions
        kvals = flex.double([
            hemisphere_solutions[x].kval
            for x in range(len(hemisphere_solutions))
        ])

        perm = flex.sort_permutation(kvals, True)

        #  need to be more clever than just taking the top 30.
        #  one huge cluster around a strong basis direction could dominate the
        #  whole hemisphere map, preventing the discovery of three basis vectors

        perm_idx = 0
        unique_clusters = 0
        hemisphere_solutions_sort = flex.Direction()
        while perm_idx < len(perm) and \
              unique_clusters < size:
            test_item = hemisphere_solutions[perm[perm_idx]]
            direction_ok = True
            for list_item in hemisphere_solutions_sort:
                distance = math.sqrt(
                    math.pow(list_item.dvec[0] - test_item.dvec[0], 2) +
                    math.pow(list_item.dvec[1] - test_item.dvec[1], 2) +
                    math.pow(list_item.dvec[2] - test_item.dvec[2], 2))
                if distance < 0.087:  #i.e., 5 degrees radius for clustering analysis
                    direction_ok = False
                    break
            if direction_ok:
                unique_clusters += 1
            hemisphere_solutions_sort.append(test_item)
            perm_idx += 1

        return hemisphere_solutions_sort
Ejemplo n.º 7
0
    def get_top_solutions(self, ai, input_directions, size, cutoff_divisor,
                          grid):
        # size was 30 in the Rossmann DPS paper

        kval_cutoff = ai.getXyzSize() / cutoff_divisor

        hemisphere_solutions = flex.Direction()
        hemisphere_solutions.reserve(size)
        for i in range(len(input_directions)):
            D = sampled_direction = ai.fft_result(input_directions[i])

            if D.real < self.max_cell_input and sampled_direction.kval > kval_cutoff:
                #directional_show(D, "ddd %5d"%i)
                hemisphere_solutions.append(sampled_direction)

        if (hemisphere_solutions.size() < 3):
            return hemisphere_solutions
        kvals = flex.double([
            hemisphere_solutions[x].kval
            for x in range(len(hemisphere_solutions))
        ])

        perm = flex.sort_permutation(kvals, True)

        #  need to be more clever than just taking the top 30.
        #  one huge cluster around a strong basis direction could dominate the
        #  whole hemisphere map, preventing the discovery of three basis vectors

        perm_idx = 0
        unique_clusters = 0
        hemisphere_solutions_sort = flex.Direction()
        while perm_idx < len(perm) and \
              unique_clusters < size:
            test_item = hemisphere_solutions[perm[perm_idx]]
            direction_ok = True
            for list_item in hemisphere_solutions_sort:
                distance = math.sqrt(
                    math.pow(list_item.dvec[0] - test_item.dvec[0], 2) +
                    math.pow(list_item.dvec[1] - test_item.dvec[1], 2) +
                    math.pow(list_item.dvec[2] - test_item.dvec[2], 2))
                if distance < 0.087:  #i.e., 5 degrees
                    direction_ok = False
                    break
            if direction_ok:
                unique_clusters += 1
                hemisphere_solutions_sort.append(test_item)
            perm_idx += 1

        return hemisphere_solutions_sort
Ejemplo n.º 8
0
  def get_top_solutions(self,ai,input_directions,size,cutoff_divisor,grid):

    kval_cutoff = ai.getXyzSize()/cutoff_divisor;

    hemisphere_solutions = flex.Direction();
    hemisphere_solutions.reserve(size);
    #print "# of input directions", len(input_directions)
    for i in xrange(len(input_directions)):
      D = sampled_direction = ai.fft_result(input_directions[i])

      #hardcoded parameter in for silicon example.  Not sure at this point how
      #robust the algorithm is when this value is relaxed.

      if D.real < self.max_cell and sampled_direction.kval > kval_cutoff:
        if diagnostic:  directional_show(D, "%5d"%i)
        hemisphere_solutions.append(sampled_direction)
    if (hemisphere_solutions.size()<3):
      return hemisphere_solutions
    kvals = flex.double([
  hemisphere_solutions[x].kval for x in xrange(len(hemisphere_solutions))])

    perm = flex.sort_permutation(kvals,True)

    #  need to be more clever than just taking the top 30.
    #  one huge cluster around a strong basis direction could dominate the
    #  whole hemisphere map, preventing the discovery of three basis vectors

    perm_idx = 0
    unique_clusters = 0
    hemisphere_solutions_sort = flex.Direction()
    while perm_idx < len(perm) and \
          unique_clusters < size:
      test_item = hemisphere_solutions[perm[perm_idx]]
      direction_ok = True
      for list_item in hemisphere_solutions_sort:
        distance = math.sqrt(math.pow(list_item.dvec[0]-test_item.dvec[0],2) +
                     math.pow(list_item.dvec[1]-test_item.dvec[1],2) +
                     math.pow(list_item.dvec[2]-test_item.dvec[2],2)
                     )
        if distance < 0.087: #i.e., 5 degrees radius for clustering analysis
           direction_ok=False
           break
      if direction_ok:
        unique_clusters+=1
      hemisphere_solutions_sort.append(test_item)
      perm_idx+=1

    return hemisphere_solutions_sort;
Ejemplo n.º 9
0
    def score_vectors(self, reciprocal_lattice_vectors):
        """Compute the functional for the given directions.

        Args:
            directions: An iterable of the search directions.
            reciprocal_lattice_vectors (scitbx.array_family.flex.vec3_double):
                The list of reciprocal lattice vectors.
        Returns:
            A tuple containing the list of search vectors and their scores.
        """
        vectors = flex.vec3_double()
        scores = flex.double()
        for i, v in enumerate(self.search_vectors):
            f = self.compute_functional(v.elems, reciprocal_lattice_vectors)
            vectors.append(v.elems)
            scores.append(f)
        return vectors, scores
Ejemplo n.º 10
0
  def get_top_solutions(self,ai,input_directions,size,cutoff_divisor,grid):
    # size was 30 in the Rossmann DPS paper

    kval_cutoff = ai.getXyzSize()/cutoff_divisor;

    hemisphere_solutions = flex.Direction();
    hemisphere_solutions.reserve(size);
    for i in xrange(len(input_directions)):
      D = sampled_direction = ai.fft_result(input_directions[i])

      if D.real < self.max_cell_input and sampled_direction.kval > kval_cutoff:
        #directional_show(D, "ddd %5d"%i)
        hemisphere_solutions.append(sampled_direction)

    if (hemisphere_solutions.size()<3):
      return hemisphere_solutions
    kvals = flex.double([
  hemisphere_solutions[x].kval for x in xrange(len(hemisphere_solutions))])

    perm = flex.sort_permutation(kvals,True)

    #  need to be more clever than just taking the top 30.
    #  one huge cluster around a strong basis direction could dominate the
    #  whole hemisphere map, preventing the discovery of three basis vectors

    perm_idx = 0
    unique_clusters = 0
    hemisphere_solutions_sort = flex.Direction()
    while perm_idx < len(perm) and \
          unique_clusters < size:
      test_item = hemisphere_solutions[perm[perm_idx]]
      direction_ok = True
      for list_item in hemisphere_solutions_sort:
        distance = math.sqrt(math.pow(list_item.dvec[0]-test_item.dvec[0],2) +
                     math.pow(list_item.dvec[1]-test_item.dvec[1],2) +
                     math.pow(list_item.dvec[2]-test_item.dvec[2],2)
                     )
        if distance < 0.087: #i.e., 5 degrees
           direction_ok=False
           break
      if direction_ok:
        unique_clusters+=1
      hemisphere_solutions_sort.append(test_item)
      perm_idx+=1

    return hemisphere_solutions_sort;
Ejemplo n.º 11
0
  def optimize_S0_local_scope(self):
      """Local scope: find the optimal S0 vector closest to the input S0 vector
         (local minimum, simple minimization)"""

      ############  Implement a direct beam check right here #########################
      unique=0
      # construct two vectors that are perpendicular to the beam.  Gives a basis for refining beam
      beamr0 = self.S0_vector.cross(self.axis).normalize()
      beamr1 = beamr0.cross(self.S0_vector).normalize()
      beamr2 = beamr1.cross(self.S0_vector).normalize()

      assert approx_equal(self.S0_vector.dot(beamr1), 0.)
      assert approx_equal(self.S0_vector.dot(beamr2), 0.)
      assert approx_equal(beamr2.dot(beamr1), 0.)
      # so the orthonormal vectors are self.S0_vector, beamr1 and beamr2

      grid = 10

      # DO A SIMPLEX MINIMIZATION
      from scitbx.simplex import simplex_opt
      class test_simplex_method(object):
        def __init__(selfOO):
          selfOO.starting_simplex=[]
          selfOO.n = 2
          for ii in range(selfOO.n+1):
            selfOO.starting_simplex.append(flex.random_double(selfOO.n))
          selfOO.optimizer = simplex_opt( dimension=selfOO.n,
                                        matrix  = selfOO.starting_simplex,
                                        evaluator = selfOO,
                                        tolerance=1e-7)
          selfOO.x = selfOO.optimizer.get_solution()

        def target(selfOO, vector):
          newvec = matrix.col(self.S0_vector) + vector[0]*0.0002*beamr1 + vector[1]*0.0002*beamr2
          normal = newvec.normalize() * self.inv_wave
          return -self.get_S0_vector_score(normal,unique) # extended API

      MIN = test_simplex_method()
      #MIN = test_cma_es()
      print "MINIMUM=",list(MIN.x)
      newvec = matrix.col(self.S0_vector) + MIN.x[0]*0.0002*beamr1 + MIN.x[1]*0.0002*beamr2
      new_S0_vector = newvec.normalize() * self.inv_wave

      print "old S0:",list(self.S0_vector.elems)
      print "new S0",list(new_S0_vector.elems)

      plot = False
      if plot:
        scores = flex.double()
        for x in range(-grid,grid+1):
         for y in range(-grid,grid+1):
          ref = matrix.col(self.S0_vector)
          newvec = ref + x*0.0002*beamr1 + y*0.0002*beamr2
          normal = newvec.normalize() * self.inv_wave
          scores.append( self.get_S0_vector_score(normal,unique) ) # extended API

        def show_plot(grid,excursi):
          excursi.reshape(flex.grid(grid, grid))

          from matplotlib import pyplot as plt
          plt.figure()
          CS = plt.contour([i*0.2 for i in range(grid)],[i*0.2 for i in range(grid)], excursi.as_numpy_array())
          plt.clabel(CS, inline=1, fontsize=10, fmt="%6.3f")
          plt.title("Score as to beam likelihood")
          plt.scatter([0.1*(grid-1)],[0.1*(grid-1)],color='g',marker='o')
          plt.scatter([0.1*(grid-1)+0.2*MIN.x[0]] , [0.1*(grid-1)+0.2*MIN.x[1]],color='r',marker='*')
          plt.axes().set_aspect("equal")
          plt.show()

        show_plot(2 * grid + 1, scores)

      return new_S0_vector
Ejemplo n.º 12
0
  def optimize_origin_offset_local_scope(self):
      """Local scope: find the optimal origin-offset closest to the current overall detector position
         (local minimum, simple minimization)"""
      # construct two vectors that are perpendicular to the beam.  Gives a basis for refining beam
      if self.axis is None:
        beamr0 = self.S0_vector.cross(matrix.col((1,0,0))).normalize()
      else:
        beamr0 = self.S0_vector.cross(self.axis).normalize()
      beamr1 = beamr0.cross(self.S0_vector).normalize()
      beamr2 = beamr1.cross(self.S0_vector).normalize()

      assert approx_equal(self.S0_vector.dot(beamr1), 0.)
      assert approx_equal(self.S0_vector.dot(beamr2), 0.)
      assert approx_equal(beamr2.dot(beamr1), 0.)
      # so the orthonormal vectors are self.S0_vector, beamr1 and beamr2

      # DO A SIMPLEX MINIMIZATION
      from scitbx.simplex import simplex_opt
      class test_simplex_method(object):
        def __init__(selfOO):
          selfOO.starting_simplex=[]
          selfOO.n = 2
          for ii in range(selfOO.n+1):
            selfOO.starting_simplex.append(flex.random_double(selfOO.n))
          selfOO.optimizer = simplex_opt( dimension=selfOO.n,
                                        matrix  = selfOO.starting_simplex,
                                        evaluator = selfOO,
                                        tolerance=1e-7)
          selfOO.x = selfOO.optimizer.get_solution()

        def target(selfOO, vector):
          trial_origin_offset = vector[0]*0.2*beamr1 + vector[1]*0.2*beamr2
          return -self.get_origin_offset_score(trial_origin_offset)

      MIN = test_simplex_method()
      trial_origin_offset =  MIN.x[0]*0.2*beamr1 + MIN.x[1]*0.2*beamr2
      #print "The Origin Offset best score is",self.get_origin_offset_score(trial_origin_offset)

      if self.horizon_phil.indexing.plot_search_scope:
        scope = self.horizon_phil.indexing.mm_search_scope
        plot_px_sz = self.detector[0].get_pixel_size()[0]
        grid = max(1,int(scope/plot_px_sz))
        scores = flex.double()
        for y in range(-grid,grid+1):
         for x in range(-grid,grid+1):
          new_origin_offset = x*plot_px_sz*beamr1 + y*plot_px_sz*beamr2
          scores.append( self.get_origin_offset_score(new_origin_offset) )

        def show_plot(widegrid,excursi):
          excursi.reshape(flex.grid(widegrid, widegrid))

          def igrid(x): return x - (widegrid//2)
          from matplotlib import pyplot as plt
          plt.figure()
          CS = plt.contour([igrid(i)*plot_px_sz for i in range(widegrid)],
                           [igrid(i)*plot_px_sz for i in range(widegrid)], excursi.as_numpy_array())
          plt.clabel(CS, inline=1, fontsize=10, fmt="%6.3f")
          plt.title("Wide scope search for detector origin offset")
          plt.scatter([0.0],[0.0],color='g',marker='o')
          plt.scatter([0.2*MIN.x[0]] , [0.2*MIN.x[1]],color='r',marker='*')
          plt.axes().set_aspect("equal")
          plt.xlabel("offset (mm) along beamr1 vector")
          plt.ylabel("offset (mm) along beamr2 vector")
          plt.show()

        show_plot(widegrid = 2 * grid + 1, excursi = scores)

      return dps_extended.get_new_detector(self.detector, trial_origin_offset)
Ejemplo n.º 13
0
  def optimize_S0_local_scope(self):
      """Local scope: find the optimal S0 vector closest to the input S0 vector
         (local minimum, simple minimization)"""

      ############  Implement a direct beam check right here #########################
      unique=0
      # construct two vectors that are perpendicular to the beam.  Gives a basis for refining beam
      beamr0 = self.S0_vector.cross(self.axis).normalize()
      beamr1 = beamr0.cross(self.S0_vector).normalize()
      beamr2 = beamr1.cross(self.S0_vector).normalize()

      assert approx_equal(self.S0_vector.dot(beamr1), 0.)
      assert approx_equal(self.S0_vector.dot(beamr2), 0.)
      assert approx_equal(beamr2.dot(beamr1), 0.)
      # so the orthonormal vectors are self.S0_vector, beamr1 and beamr2

      grid = 10

      # DO A SIMPLEX MINIMIZATION
      from scitbx.simplex import simplex_opt
      class test_simplex_method(object):
        def __init__(selfOO):
          selfOO.starting_simplex=[]
          selfOO.n = 2
          for ii in range(selfOO.n+1):
            selfOO.starting_simplex.append(flex.random_double(selfOO.n))
          selfOO.optimizer = simplex_opt( dimension=selfOO.n,
                                        matrix  = selfOO.starting_simplex,
                                        evaluator = selfOO,
                                        tolerance=1e-7)
          selfOO.x = selfOO.optimizer.get_solution()

        def target(selfOO, vector):
          newvec = matrix.col(self.S0_vector) + vector[0]*0.0002*beamr1 + vector[1]*0.0002*beamr2
          normal = newvec.normalize() * self.inv_wave
          return -self.get_S0_vector_score(normal,unique) # extended API

      MIN = test_simplex_method()
      #MIN = test_cma_es()
      print "MINIMUM=",list(MIN.x)
      newvec = matrix.col(self.S0_vector) + MIN.x[0]*0.0002*beamr1 + MIN.x[1]*0.0002*beamr2
      new_S0_vector = newvec.normalize() * self.inv_wave

      print "old S0:",list(self.S0_vector.elems)
      print "new S0",list(new_S0_vector.elems)

      plot = False
      if plot:
        scores = flex.double()
        for x in xrange(-grid,grid+1):
         for y in xrange(-grid,grid+1):
          ref = matrix.col(self.S0_vector)
          newvec = ref + x*0.0002*beamr1 + y*0.0002*beamr2
          normal = newvec.normalize() * self.inv_wave
          scores.append( self.get_S0_vector_score(normal,unique) ) # extended API

        def show_plot(grid,excursi):
          excursi.reshape(flex.grid(grid, grid))

          from matplotlib import pyplot as plt
          plt.figure()
          CS = plt.contour([i*0.2 for i in xrange(grid)],[i*0.2 for i in xrange(grid)], excursi.as_numpy_array())
          plt.clabel(CS, inline=1, fontsize=10, fmt="%6.3f")
          plt.title("Score as to beam likelihood")
          plt.scatter([0.1*(grid-1)],[0.1*(grid-1)],color='g',marker='o')
          plt.scatter([0.1*(grid-1)+0.2*MIN.x[0]] , [0.1*(grid-1)+0.2*MIN.x[1]],color='r',marker='*')
          plt.axes().set_aspect("equal")
          plt.show()

        show_plot(2 * grid + 1, scores)

      return new_S0_vector
Ejemplo n.º 14
0
  def optimize_origin_offset_local_scope(self):
      """Local scope: find the optimal origin-offset closest to the current overall detector position
         (local minimum, simple minimization)"""
      # construct two vectors that are perpendicular to the beam.  Gives a basis for refining beam
      if self.axis is None:
        beamr0 = self.S0_vector.cross(matrix.col((1,0,0))).normalize()
      else:
        beamr0 = self.S0_vector.cross(self.axis).normalize()
      beamr1 = beamr0.cross(self.S0_vector).normalize()
      beamr2 = beamr1.cross(self.S0_vector).normalize()

      assert approx_equal(self.S0_vector.dot(beamr1), 0.)
      assert approx_equal(self.S0_vector.dot(beamr2), 0.)
      assert approx_equal(beamr2.dot(beamr1), 0.)
      # so the orthonormal vectors are self.S0_vector, beamr1 and beamr2

      # DO A SIMPLEX MINIMIZATION
      from scitbx.simplex import simplex_opt
      class test_simplex_method(object):
        def __init__(selfOO):
          selfOO.starting_simplex=[]
          selfOO.n = 2
          for ii in range(selfOO.n+1):
            selfOO.starting_simplex.append(flex.random_double(selfOO.n))
          selfOO.optimizer = simplex_opt( dimension=selfOO.n,
                                        matrix  = selfOO.starting_simplex,
                                        evaluator = selfOO,
                                        tolerance=1e-7)
          selfOO.x = selfOO.optimizer.get_solution()

        def target(selfOO, vector):
          trial_origin_offset = vector[0]*0.2*beamr1 + vector[1]*0.2*beamr2
          return -self.get_origin_offset_score(trial_origin_offset)

      MIN = test_simplex_method()
      trial_origin_offset =  MIN.x[0]*0.2*beamr1 + MIN.x[1]*0.2*beamr2
      #print "The Origin Offset best score is",self.get_origin_offset_score(trial_origin_offset)

      if self.horizon_phil.indexing.plot_search_scope:
        scope = self.horizon_phil.indexing.mm_search_scope
        plot_px_sz = self.detector[0].get_pixel_size()[0]
        grid = max(1,int(scope/plot_px_sz))
        scores = flex.double()
        for y in xrange(-grid,grid+1):
         for x in xrange(-grid,grid+1):
          new_origin_offset = x*plot_px_sz*beamr1 + y*plot_px_sz*beamr2
          scores.append( self.get_origin_offset_score(new_origin_offset) )

        def show_plot(widegrid,excursi):
          excursi.reshape(flex.grid(widegrid, widegrid))

          def igrid(x): return x - (widegrid//2)
          from matplotlib import pyplot as plt
          plt.figure()
          CS = plt.contour([igrid(i)*plot_px_sz for i in xrange(widegrid)],
                           [igrid(i)*plot_px_sz for i in xrange(widegrid)], excursi.as_numpy_array())
          plt.clabel(CS, inline=1, fontsize=10, fmt="%6.3f")
          plt.title("Wide scope search for detector origin offset")
          plt.scatter([0.0],[0.0],color='g',marker='o')
          plt.scatter([0.2*MIN.x[0]] , [0.2*MIN.x[1]],color='r',marker='*')
          plt.axes().set_aspect("equal")
          plt.xlabel("offset (mm) along beamr1 vector")
          plt.ylabel("offset (mm) along beamr2 vector")
          plt.show()

        show_plot(widegrid = 2 * grid + 1, excursi = scores)

      return dps_extended.get_new_detector(self.detector, trial_origin_offset)