Example #1
0
def _isNearPosition(current_pos, target_position, axes):
    """
    Check whether given axis is near stage target position
    :param current_pos: (dict) current position dict (axis -> value)
    :param target_position: (dict) target position dict (axis -> value)
    :param axes: (set) axes to compare values
    :return: True if the axis is near position, False otherwise
    :raises ValueError if axis is unknown
    """
    if not axes:
        logging.warning("Empty axes given.")
        return False
    for axis in axes:
        current_value = current_pos[axis]
        target_value = target_position[axis]
        if axis in {'x', 'y', 'z'}:
            is_near = abs(target_value - current_value) < ATOL_LINEAR_POS
        elif axis in {'rx', 'rz'}:
            is_near = util.rot_almost_equal(current_value,
                                            target_value,
                                            atol=ATOL_ROTATION_POS)
        else:
            raise ValueError("Unknown axis value %s." % axis)
        if not is_near:
            return False
    return True
Example #2
0
 def test_rot_atol(self):
     in_exp = {
         (0.1, 0, 0.2): True,
         (2 * math.pi, 6.28, 0.01): True,
         (2 * math.pi + 1e-6, 4 * math.pi, 10e-6): True,
         (3 * math.pi + 1e-6, 5 * math.pi, 10e-6): True,
         (-2 * math.pi - 1e-6, 4 * math.pi, 10e-6): True,
         (-2 * math.pi - 20e-6, 4 * math.pi, 10e-6): False,
     }
     for i, eo in in_exp.items():
         o = util.rot_almost_equal(*i)
         self.assertEqual(o, eo,
                          "Failed to get correct output for %s" % (i, ))
Example #3
0
 def test_rot_rtol(self):
     in_exp = {
         (0.1, 0.11, 0.2): True,
         (0.1, 0.11, 0.01): False,
         (-0.1, -0.11, 0.01): False,
         (2 * math.pi, 6.28, 0.1): False,  # values very close from 0
         (3 * math.pi + 1e-6, 5 * math.pi, 0.01):
         True,  # values very far from 0
         (2001 * math.pi + 1e-6, 5 * math.pi, 0.01): True,
         (2000 * math.pi + 1e-6, 5 * math.pi, 0.01): False,
     }
     for i, eo in in_exp.items():
         o = util.rot_almost_equal(i[0], i[1], rtol=i[2])
         self.assertEqual(o, eo,
                          "Failed to get correct output for %s" % (i, ))
Example #4
0
 def test_rot_simple(self):
     in_exp = {
         (0., 0): True,
         (-5, -5.): True,
         (1., 1. - 1e-9): True,
         (1., 1. - 1e-3): False,
         (1., 1. + 1e-3): False,
         (2 * math.pi, 6.28): False,
         (2 * math.pi, 4 * math.pi): True,
         (3 * math.pi, 5 * math.pi): True,
         (-2 * math.pi, 4 * math.pi): True,
     }
     for i, eo in in_exp.items():
         o = util.rot_almost_equal(*i)
         self.assertEqual(o, eo,
                          "Failed to get correct output for %s" % (i, ))
Example #5
0
def EstimateLatticeConstant(pos):
    """
    Estimate the lattice constant of a point set that represent a square grid.
    The lattice constant refers to the physical dimension of unit cells in a
    crystal lattice. It is the physical dimension of the smallest repeating
    unit that possesses all the symmetry of the crystal structure.

    It is used here to estimate the physical dimension in i and j of the
    smallest repeating unit of a point set in a square grid.

    Parameters
    ----------
    pos : array like
        A 2D array of shape (N, 2) containing the (x, y) coordinates of the points.
        Where N must be at least 5.

    Returns
    -------
    lattice_constants : array like [2x2]
        The lattice constants. The first row corresponds to the (x, y) direction of the fist lattice constant,
        the second row corresponds to the (x, y) direction of the second lattice constant.

    """
    if len(pos) < 5:
        # Because the lattice constants are based on the 4 nearest neighbours of a point,
        # a total of 5 points is needed to estimate the lattice constants.
        raise ValueError(
            "Need at least 5 positions to estimate the lattice constants")
    # Find the closest 4 neighbours (excluding itself) for each point.
    tree = KDTree(pos)
    dd, ii = tree.query(pos, k=5)
    dr = dd[:, 1:]  # exclude the point itself

    # Determine the median radial distance and filter all points beyond
    # 2*sigma.
    med = numpy.median(dr)
    std = numpy.std(dr)
    outliers = numpy.abs(dr - med) > (
        2 * std)  # doesn't work well if std is very high

    # Determine horizontal and vertical distance from the point itself to its 4
    # closest neighbours (only radial distance is returned by tree.query).
    dpos = pos[ii[:, 0, numpy.newaxis]] - pos[ii[:, 1:]]
    dx, dy = dpos[:, :, 0], dpos[:, :, 1]
    assert numpy.all(numpy.abs(dr - numpy.hypot(dx, dy)) < 1.0e-12)
    # Stack the x and y directions from a point to its 4 nearest neighbours.
    X = numpy.column_stack((dx[~outliers], dy[~outliers]))
    # Make sure the x and y directions are positive.
    X[X[:, 0] < -0.5 * med] *= -1
    X[X[:, 1] < -0.5 * med] *= -1

    # Use k-means to group the points into the two most common directions.
    centroids, _ = kmeans(X, 2)
    # For each point add a label of which of the two most common directions it belongs to.
    labels = numpy.argmin(cdist(X, centroids), axis=1)
    # Find the median of each of the two most common directions.
    lattice_constants = numpy.array([
        numpy.median(X[labels.ravel() == 0], axis=0),
        numpy.median(X[labels.ravel() == 1], axis=0)
    ])

    # The angle between the two directions should be close to 90 degrees.
    alpha = numpy.math.atan2(
        numpy.linalg.norm(numpy.cross(*lattice_constants)),
        numpy.dot(*lattice_constants))
    if not util.rot_almost_equal(alpha, math.pi / 2, math.radians(2.5)):
        logging.warning('Estimated lattice angle differs from 90 degrees by '
                        'more than 2.5 degrees. Input data could be wrong')
    if numpy.linalg.det(lattice_constants) < 0.:
        # A negative determinant means the first axis is rotated 90 degrees CCW compared to the second.
        # Flip to make the lattice constants consistent with the pos input.
        lattice_constants = numpy.flipud(lattice_constants)

    return lattice_constants