コード例 #1
0
def test_get_rotation_matrix_between_vectors(from_v1, from_v2, to_v1, to_v2,
                                             expected_rotation):
    rotation_matrix = get_rotation_matrix_between_vectors(
        np.array(from_v1), np.array(from_v2), np.array([to_v1]),
        np.array([to_v2]))
    np.testing.assert_allclose(rotation_matrix,
                               np.array([expected_rotation]),
                               atol=1e-15)
コード例 #2
0
ファイル: indexation_utils.py プロジェクト: sigao1988/pyxem
def match_vectors(peaks, library, mag_tol, angle_tol, index_error_tol,
                  n_peaks_to_index, n_best):
    # TODO: Sort peaks by intensity or SNR
    """Assigns hkl indices to pairs of diffraction vectors.

    Parameters
    ----------
    peaks : np.array()
        The experimentally measured diffraction vectors, associated with a
        particular probe position, to be indexed. In Cartesian coordinates.
    library : VectorLibrary
        Library of reciprocal space vectors to be matched to the vectors.
    mag_tol : float
        Max allowed magnitude difference when comparing vectors.
    angle_tol : float
        Max allowed angle difference in radians when comparing vector pairs.
    index_error_tol : float
        Max allowed error in peak indexation for classifying it as indexed,
        calculated as :math:`|hkl_calculated - round(hkl_calculated)|`.
    n_peaks_to_index : int
        The maximum number of peak to index.
    n_best : int
        The maximum number of good solutions to be retained for each phase.

    Returns
    -------
    indexation : np.array()
        A numpy array containing the indexation results, each result consisting of 5 entries:
            [phase index, rotation matrix, match rate, error hkls, total error]

    """
    if peaks.shape == (1, ) and peaks.dtype == np.object:
        peaks = peaks[0]

    # Assign empty array to hold indexation results. The n_best best results
    # from each phase is returned.
    top_matches = np.empty(len(library) * n_best, dtype="object")
    res_rhkls = []

    # Iterate over phases in DiffractionVectorLibrary and perform indexation
    # on each phase, storing the best results in top_matches.
    for phase_index, (phase, structure) in enumerate(
            zip(library.values(), library.structures)):
        solutions = []
        lattice_recip = structure.lattice.reciprocal()
        phase_indices = phase["indices"]
        phase_measurements = phase["measurements"]

        if peaks.shape[0] < 2:  # pragma: no cover
            continue

        # Choose up to n_peaks_to_index unindexed peaks to be paired in all
        # combinations.
        # TODO: Matching can be done iteratively where successfully indexed
        #       peaks are removed after each iteration. This can possibly
        #       handle overlapping patterns.
        # unindexed_peak_ids = range(min(peaks.shape[0], n_peaks_to_index))
        # TODO: Better choice of peaks (longest, highest SNR?)
        # TODO: Inline after choosing the best, and possibly require external sorting (if using sorted)?
        unindexed_peak_ids = _choose_peak_ids(peaks, n_peaks_to_index)

        # Find possible solutions for each pair of peaks.
        for vector_pair_index, peak_pair_indices in enumerate(
                list(combinations(unindexed_peak_ids, 2))):
            # Consider a pair of experimental scattering vectors.
            q1, q2 = peaks[peak_pair_indices, :]
            q1_len, q2_len = np.linalg.norm(q1), np.linalg.norm(q2)

            # Ensure q1 is longer than q2 for consistent order.
            if q1_len < q2_len:
                q1, q2 = q2, q1
                q1_len, q2_len = q2_len, q1_len

            # Calculate the angle between experimental scattering vectors.
            angle = get_angle_cartesian(q1, q2)

            # Get library indices for hkls matching peaks within tolerances.
            # TODO: phase are object arrays. Test performance of direct float arrays
            tolerance_mask = np.abs(phase_measurements[:, 0] -
                                    q1_len) < mag_tol
            tolerance_mask[tolerance_mask] &= (
                np.abs(phase_measurements[tolerance_mask, 1] - q2_len) <
                mag_tol)
            tolerance_mask[tolerance_mask] &= (
                np.abs(phase_measurements[tolerance_mask, 2] - angle) <
                angle_tol)

            # Iterate over matched library vectors determining the error in the
            # associated indexation.
            if np.count_nonzero(tolerance_mask) == 0:
                continue

            # Reference vectors are cartesian coordinates of hkls
            reference_vectors = lattice_recip.cartesian(
                phase_indices[tolerance_mask])

            # Rotation from experimental to reference frame
            rotations = get_rotation_matrix_between_vectors(
                q1, q2, reference_vectors[:, 0], reference_vectors[:, 1])

            # Index the peaks by rotating them to the reference coordinate
            # system. Use rotation directly since it is multiplied from the
            # right. Einsum gives list of peaks.dot(rotation).
            hklss = lattice_recip.fractional(
                np.einsum("ijk,lk->ilj", rotations, peaks))

            # Evaluate error of peak hkl indexation
            rhklss = np.rint(hklss)
            ehklss = np.abs(hklss - rhklss)
            valid_peak_mask = np.max(ehklss, axis=-1) < index_error_tol
            valid_peak_counts = np.count_nonzero(valid_peak_mask, axis=-1)
            error_means = ehklss.mean(axis=(1, 2))

            num_peaks = len(peaks)
            match_rates = (valid_peak_counts *
                           (1 / num_peaks)) if num_peaks else 0

            possible_solution_mask = match_rates > 0
            solutions += [
                OrientationResult(
                    phase_index=phase_index,
                    rotation_matrix=R,
                    match_rate=match_rate,
                    error_hkls=ehkls,
                    total_error=error_mean,
                    scale=1.0,
                    center_x=0.0,
                    center_y=0.0,
                ) for R, match_rate, ehkls, error_mean in zip(
                    rotations[possible_solution_mask],
                    match_rates[possible_solution_mask],
                    ehklss[possible_solution_mask],
                    error_means[possible_solution_mask],
                )
            ]

            res_rhkls += rhklss[possible_solution_mask].tolist()

        n_solutions = min(n_best, len(solutions))

        i = phase_index * n_best  # starting index in unfolded array

        if n_solutions > 0:
            top_n = sorted(solutions,
                           key=attrgetter("match_rate"),
                           reverse=True)[:n_solutions]

            # Put the top n ranked solutions in the output array
            top_matches[i:i + n_solutions] = top_n

        if n_solutions < n_best:
            # Fill with dummy values
            top_matches[i + n_solutions:i + n_best] = [
                OrientationResult(
                    phase_index=0,
                    rotation_matrix=np.identity(3),
                    match_rate=0.0,
                    error_hkls=np.array([]),
                    total_error=1.0,
                    scale=1.0,
                    center_x=0.0,
                    center_y=0.0,
                ) for x in range(n_best - n_solutions)
            ]

    # Because of a bug in numpy (https://github.com/numpy/numpy/issues/7453),
    # triggered by the way HyperSpy reads results (np.asarray(res), which fails
    # when the two tuple values have the same first dimension), we cannot
    # return a tuple directly, but instead have to format the result as an
    # array ourselves.
    res = np.empty(2, dtype=np.object)
    res[0] = top_matches
    res[1] = np.asarray(res_rhkls)
    return res
コード例 #3
0
def test_get_rotation_matrix_between_vectors(k1, k2, ref_k1, ref_k2,
                                             expected_rotation):
    rotation_matrix = get_rotation_matrix_between_vectors(
        k1, k2, ref_k1, ref_k2)
    assert np.allclose(rotation_matrix, expected_rotation)
コード例 #4
0
def match_vectors(peaks,
                  library,
                  mag_tol,
                  angle_tol,
                  index_error_tol,
                  n_peaks_to_index,
                  n_best,
                  keys=[],
                  *args,
                  **kwargs):
    """Assigns hkl indices to pairs of diffraction vectors.

    Parameters
    ----------
    peaks : np.array()
        The experimentally measured diffraction vectors, associated with a
        particular probe position, to be indexed. In Cartesian coordinates.
    library : VectorLibrary
        Library of reciprocal space vectors to be matched to the vectors.
    mag_tol : float
        Max allowed magnitude difference when comparing vectors.
    angle_tol : float
        Max allowed angle difference in radians when comparing vector pairs.
    index_error_tol : float
        Max allowed error in peak indexation for classifying it as indexed,
        calculated as |hkl_calculated - round(hkl_calculated)|.
    n_peaks_to_index : int
        The maximum number of peak to index.
    n_best : int
        The maximum number of good solutions to be retained.

    Returns
    -------
    indexation : np.array()
        A numpy array containing the indexation results, each result consisting of 5 entries:
            [phase index, rotation matrix, match rate, error hkls, total error]

    """
    if peaks.shape == (1, ) and peaks.dtype == 'object':
        peaks = peaks[0]
    # Initialise for loop with first entry & assign empty array to hold
    # indexation results.
    top_matches = np.empty((len(library), n_best, 5), dtype='object')
    res_rhkls = []
    # TODO: Sort these by intensity or SNR

    # Iterate over phases in DiffractionVectorLibrary and perform indexation
    # with respect to each phase.
    for phase_index, (phase_name, structure) in enumerate(
            zip(library.keys(), library.structures)):
        solutions = []
        lattice_recip = structure.lattice.reciprocal()

        # Choose up to n_peaks_to_index unindexed peaks to be paired in all
        # combinations
        unindexed_peak_ids = range(min(peaks.shape[0], n_peaks_to_index))

        # Determine overall indexations associated with each peak pair
        for peak_pair_indices in combinations(unindexed_peak_ids, 2):
            # Consider a pair of experimental scattering vectors.
            q1, q2 = peaks[peak_pair_indices, :]
            q1_len, q2_len = np.linalg.norm(q1), np.linalg.norm(q2)

            # Ensure q1 is longer than q2 so combinations in correct order.
            if q1_len < q2_len:
                q1, q2 = q2, q1
                q1_len, q2_len = q2_len, q1_len

            # Calculate the angle between experimental scattering vectors.
            angle = get_angle_cartesian(q1, q2)

            # Get library indices for hkls matching peaks within tolerances.
            # TODO: Library[key] are object arrays. Test performance of direct float arrays
            # TODO: Test performance with short circuiting (np.where for each step)
            match_ids = np.where(
                (np.abs(q1_len - library[phase_name][:, 2]) < mag_tol)
                & (np.abs(q2_len - library[phase_name][:, 3]) < mag_tol)
                & (np.abs(angle - library[phase_name][:, 4]) < angle_tol))[0]

            # Iterate over matched library vectors determining the error in the
            # associated indexation and finding the minimum error cases.
            peak_pair_solutions = []
            for i, match_id in enumerate(match_ids):
                hkl1, hkl2 = library[phase_name][:, :2][match_id]
                # Reference vectors are cartesian coordinates of hkls
                ref_q1, ref_q2 = lattice_recip.cartesian(
                    hkl1), lattice_recip.cartesian(hkl2)

                # Rotation from ref to experimental
                R = get_rotation_matrix_between_vectors(q1, q2, ref_q1, ref_q2)

                # Index the peaks by rotating them to the reference coordinate
                # system. R is used directly since it is multiplied from the
                # right.
                cartesian_to_index = structure.lattice.base
                hkls = lattice_recip.fractional(peaks.dot(R))

                # Evaluate error of peak hkl indexation and total error.
                rhkls = np.rint(hkls)
                ehkls = np.abs(hkls - rhkls)
                res_rhkls.append(rhkls)

                # Indices of matched peaks within error tolerance
                pair_ids = np.where(np.max(ehkls, axis=1) < index_error_tol)[0]
                # TODO: SPIND allows trying to match multiple crystals
                # (overlap) by iteratively matching until match_rate == 0 on
                # the unindexed peaks
                # pair_ids = list(set(pair_ids) - set(indexed_peak_ids))

                # calculate match_rate as fraction of peaks indexed
                num_pairs = len(pair_ids)
                num_peaks = len(peaks)
                match_rate = num_pairs / num_peaks

                if len(pair_ids) == 0:
                    # no matching peaks, set error to 1
                    total_error = 1.0
                else:
                    # naive error of matching peaks
                    total_error = ehkls[pair_ids].mean()

                peak_pair_solutions.append([R, match_rate, ehkls, total_error])
            solutions += peak_pair_solutions

        # TODO: Intersect the solutions from each pair based on orientation.
        #       If there is only one in the intersection, assume that this is
        #       the correct crystal.
        # TODO: SPIND sorts by highest match rate then lowest total error and
        #       returns the single best solution. Here, we instead return the n
        #       best solutions. Correct approach for pyXem?
        #       best_match_rate_solutions = solutions[solutions[6].argmax()]
        n_solutions = min(n_best, len(solutions))
        if n_solutions > 0:
            match_rate_index = 1
            solutions = np.array(solutions)
            top_n = solutions[solutions[:, match_rate_index].argpartition(
                -n_solutions)[-n_solutions:]]

            # Put the top n ranked solutions in the output array
            top_matches[phase_index, :, 0] = phase_index
            top_matches[phase_index, :n_solutions, 1:] = top_n

        if n_solutions < n_best:
            # Fill with dummy values
            top_matches[phase_index, n_solutions:] = [
                0, np.identity(3), 0, np.array([]), 1.0
            ]

        # TODO: Refine?

    # Because of a bug in numpy (https://github.com/numpy/numpy/issues/7453),
    # triggered by the way HyperSpy reads results (np.asarray(res), which fails
    # when the two tuple values have the same first dimension), we cannot
    # return a tuple directly, but instead have to format the result as an
    # array ourselves.
    res = np.empty(2, dtype='object')
    res[0] = top_matches.reshape((len(library) * n_best, 5))
    res[1] = np.asarray(res_rhkls)
    return res