Esempio n. 1
0
    def test_hesse_lines(self, nickel_phase):
        """Desired Hesse lines."""
        full_shape = (2, 2, 2)
        bands = KikuchiBand(
            phase=nickel_phase,
            hkl=np.array([[-1, 1, 1], [0, -2, 0]]),
            hkl_detector=np.tile(
                np.array([[0.26, 0.32, 0.26], [-0.21, 0.45, -0.27]]),
                full_shape + (1, 1),
            ),
            in_pattern=np.ones(full_shape, dtype=bool),
        )

        assert np.allclose(
            bands.hesse_line_x,
            np.array([
                [[-0.39764706, -0.22992701], [-0.39764706, -0.22992701]],
                [[-0.39764706, -0.22992701], [-0.39764706, -0.22992701]],
            ]),
        )

        assert np.allclose(
            bands.hesse_line_y,
            np.array([
                [[-0.48941176, 0.49270073], [-0.48941176, 0.49270073]],
                [[-0.48941176, 0.49270073], [-0.48941176, 0.49270073]],
            ]),
        )
Esempio n. 2
0
    def test_getitem(
        self,
        nickel_phase,
        nav_shape,
        n_bands,
        get_key,
        desired_data_shape_before,
        desired_data_shape_after,
    ):
        """Slicing works as expected."""
        hkl = np.repeat(np.arange(1, n_bands + 1), 3).reshape((n_bands, 3))
        hkl_detector = np.tile(
            np.repeat(np.arange(1, n_bands + 1), 3).reshape((n_bands, 3)),
            nav_shape + (1, 1),
        )  # shape nav_shape + (size, 3)
        bands = KikuchiBand(
            phase=nickel_phase,
            hkl=hkl,
            hkl_detector=hkl_detector,
            in_pattern=np.ones(nav_shape + (n_bands, ), dtype=bool),
        )

        assert bands._data_shape == desired_data_shape_before

        new_bands = bands[get_key]
        assert new_bands._data_shape == desired_data_shape_after
Esempio n. 3
0
    def test_plane_trace_coordinates(
        self,
        nickel_phase,
        hkl,
        hkl_detector,
        gnomonic_radius,
        plane_trace_coordinates,
    ):
        """Desired gnomonic (P1, P2) coordinates."""
        bands = KikuchiBand(
            phase=nickel_phase,
            hkl=hkl,
            hkl_detector=hkl_detector,
            in_pattern=np.ones(hkl_detector.shape[:-1], dtype=bool),
            gnomonic_radius=gnomonic_radius,
        )

        assert (bands.plane_trace_coordinates.shape ==
                plane_trace_coordinates.shape)
        assert np.allclose(
            bands.plane_trace_coordinates,
            plane_trace_coordinates,
            atol=1e-4,
            equal_nan=True,
        )
Esempio n. 4
0
 def test_repr(self, nickel_phase, hkl, hkl_detector, dim_str, band_str):
     """Desired representation."""
     bands = KikuchiBand(
         phase=nickel_phase,
         hkl=hkl,
         hkl_detector=hkl_detector,
         in_pattern=np.ones(hkl_detector.shape[:-1], dtype=bool),
     )
     assert repr(bands) == (
         f"KikuchiBand {dim_str}\n"
         f"Phase: {nickel_phase.name} ({nickel_phase.point_group.name})\n"
         f"{band_str}")
Esempio n. 5
0
def nickel_kikuchi_band(nickel_rlp, nickel_rotations, pc1):
    rlp = nickel_rlp.symmetrise()

    phase = rlp.phase
    hkl = rlp.hkl.data

    nav_shape = (5, 5)

    detector = EBSDDetector(
        shape=(60, 60),
        binning=8,
        px_size=70,
        pc=np.ones(nav_shape + (3, )) * pc1,
        sample_tilt=70,
        tilt=0,
        convention="tsl",
    )

    nav_dim = detector.navigation_dimension
    navigation_axes = (1, 2)[:nav_dim]

    # Output shape is (3, n, 3) or (3, ny, nx, 3)
    det2recip = detector2reciprocal_lattice(
        sample_tilt=detector.sample_tilt,
        detector_tilt=detector.tilt,
        lattice=phase.structure.lattice,
        rotation=nickel_rotations.reshape(*nav_shape),
    )

    # Output shape is (nhkl, n, 3) or (nhkl, ny, nx, 3)
    hkl_detector = np.tensordot(hkl, det2recip, axes=(1, 0))
    # Determine whether a band is visible in a pattern
    upper_hemisphere = hkl_detector[..., 2] > 0
    is_in_some_pattern = np.sum(upper_hemisphere, axis=navigation_axes) != 0
    # Get bands that are in some pattern and their coordinates in the
    # proper shape
    hkl = hkl[is_in_some_pattern, ...]
    hkl_in_pattern = upper_hemisphere[is_in_some_pattern, ...].T
    hkl_detector = np.moveaxis(hkl_detector[is_in_some_pattern],
                               source=0,
                               destination=nav_dim)

    return KikuchiBand(
        phase=phase,
        hkl=hkl,
        hkl_detector=hkl_detector,
        in_pattern=hkl_in_pattern,
        gnomonic_radius=detector.r_max,
    )
Esempio n. 6
0
    def geometrical_simulation(
        self,
        reciprocal_lattice_point: Optional[ReciprocalLatticePoint] = None
    ) -> GeometricalEBSDSimulation:
        """Project a set of Kikuchi bands and zone axes onto the
        detector, one set for each rotation of the unit cell.

        The zone axes are calculated from the Kikuchi bands.

        Parameters
        ----------
        reciprocal_lattice_point
            Crystal planes to project onto the detector. If None, and
            the generator has a phase with a unit cell with a point
            group, a set of planes with minimum distance of 1 Å and
            their symmetrically equivalent planes are used.

        Returns
        -------
        GeometricalEBSDSimulation

        Examples
        --------
        >>> from diffsims.crystallography import ReciprocalLatticePoint
        >>> simgen
        EBSDSimulationGenerator (1,)
        EBSDDetector (60, 60), px_size 1 um, binning 1, tilt 0, pc
        (0.5, 0.5, 0.5)
        <name: . space group: None. point group: None. proper point
        group: None. color: tab:blue>
        Rotation (1,)
        >>> sim1 = simgen.geometrical_simulation()
        >>> sim1.bands.size
        94
        >>> rlp = ReciprocalLatticePoint(
        ...     phase=simgen.phase, hkl=[[1, 1, 1], [2, 0, 0]]
        ... )
        >>> sim2 = simgen.geometrical_simulation()
        >>> sim2.bands.size
        13
        """
        rlp = reciprocal_lattice_point
        if rlp is None and (hasattr(self.phase.point_group, "name") and
                            hasattr(self.phase.structure.lattice, "abcABG")):
            rlp = ReciprocalLatticePoint.from_min_dspacing(self.phase,
                                                           min_dspacing=1)
            rlp = rlp[rlp.allowed].symmetrise()
        elif rlp is None:
            raise ValueError("A ReciprocalLatticePoint object must be passed")
        self._rlp_phase_is_compatible(rlp)

        # Unit cell parameters (called more than once)
        phase = rlp.phase
        hkl = rlp._hkldata

        # Get number of navigation dimensions and navigation axes
        # indices
        n_nav_dims = self.navigation_dimension
        navigation_axes = (1, 2)[:n_nav_dims]

        # Get Kikuchi band coordinates for all bands in all patterns
        # U_Kstar, transformation from detector frame D to reciprocal
        # crystal lattice frame Kstar.
        # Output shape is (3, n, 3) or (3, ny, nx, 3)
        det2recip = detector2reciprocal_lattice(
            sample_tilt=self.detector.sample_tilt,
            detector_tilt=self.detector.tilt,
            lattice=phase.structure.lattice,
            rotation=self.rotations,
        )
        # Output shape is (nhkl, n, 3) or (nhkl, ny, nx, 3)
        hkl_detector = np.tensordot(hkl, det2recip, axes=(1, 0))
        if n_nav_dims == 0:
            hkl_detector = hkl_detector.squeeze()
        # Get bands that are in some pattern
        hkl_is_upper, hkl_in_a_pattern = _get_coordinates_in_upper_hemisphere(
            z_coordinates=hkl_detector[..., 2],
            navigation_axes=navigation_axes)
        hkl = hkl[hkl_in_a_pattern, ...]
        hkl_in_pattern = hkl_is_upper[hkl_in_a_pattern, ...].T
        # Get coordinates in the proper shape
        hkl_detector = np.moveaxis(hkl_detector[hkl_in_a_pattern],
                                   source=0,
                                   destination=n_nav_dims)
        # And store it all
        bands = KikuchiBand(
            phase=phase,
            hkl=hkl,
            hkl_detector=hkl_detector,
            in_pattern=hkl_in_pattern,
            gnomonic_radius=self.detector.r_max,
        )

        # Get zone axes coordinates from Kikuchi bands
        uvw = _get_uvw_from_hkl(hkl=hkl)
        det2direct = detector2direct_lattice(
            sample_tilt=self.detector.sample_tilt,
            detector_tilt=self.detector.tilt,
            lattice=self.phase.structure.lattice,
            rotation=self.rotations,
        )
        uvw_detector = np.tensordot(uvw, det2direct, axes=(1, 0))
        if n_nav_dims == 0:
            uvw_detector = uvw_detector.squeeze()
        uvw_is_upper, uvw_in_a_pattern = _get_coordinates_in_upper_hemisphere(
            z_coordinates=uvw_detector[..., 2],
            navigation_axes=navigation_axes)
        uvw = uvw[uvw_in_a_pattern, ...]
        uvw_in_pattern = uvw_is_upper[uvw_in_a_pattern, ...].T
        uvw_detector = np.moveaxis(uvw_detector[uvw_in_a_pattern],
                                   source=0,
                                   destination=n_nav_dims)
        zone_axes = ZoneAxis(
            phase=phase,
            uvw=uvw,
            uvw_detector=uvw_detector,
            in_pattern=uvw_in_pattern,
            gnomonic_radius=self.detector.r_max,
        )

        return GeometricalEBSDSimulation(
            detector=self.detector,
            rotations=self.rotations,
            bands=bands,
            zone_axes=zone_axes,
        )
Esempio n. 7
0
    def test_init(
        self,
        nickel_phase,
        hkl,
        hkl_detector,
        within_gnomonic_radius,
        hesse_distance,
        hesse_alpha,
        nav_shape,
        nav_dim,
        size,
    ):
        """Desired initialization behaviour."""
        in_pattern = np.ones(hkl_detector.shape[:-1], dtype=bool)
        gnomonic_radius = 10
        bands = KikuchiBand(
            phase=nickel_phase,
            hkl=hkl,
            hkl_detector=hkl_detector,
            in_pattern=in_pattern,
            gnomonic_radius=gnomonic_radius,
        )

        # Correct number of bands and navigation points and their shape
        assert bands.navigation_shape == nav_shape
        assert bands.navigation_dimension == nav_dim
        assert bands.size == size

        # No rounding or anything
        assert np.allclose(bands.hkl.data, hkl)
        assert np.allclose(bands.hkl_detector.data, hkl_detector)

        # Correct detector and gnomonic coordinates
        assert np.allclose(bands.x_detector, hkl_detector[..., 0])
        assert np.allclose(bands.y_detector, hkl_detector[..., 1])
        assert np.allclose(bands.z_detector, hkl_detector[..., 2])
        assert np.allclose(bands.x_gnomonic,
                           hkl_detector[..., 0] / hkl_detector[..., 2])
        assert np.allclose(bands.y_gnomonic,
                           hkl_detector[..., 1] / hkl_detector[..., 2])

        # Whether bands are in points are treated correctly
        assert bands.in_pattern.shape == in_pattern.shape
        assert np.allclose(bands.in_pattern, in_pattern)

        # Broadcasting of gnomonic radius is correct
        gnomonic_radius2 = np.atleast_1d(gnomonic_radius * np.ones(nav_shape))
        assert bands.gnomonic_radius.shape == gnomonic_radius2.shape
        assert np.allclose(bands.gnomonic_radius, gnomonic_radius2)

        # Whether a band should be plotted or not
        assert (
            bands.within_gnomonic_radius.shape == within_gnomonic_radius.shape)
        assert np.allclose(bands.within_gnomonic_radius,
                           within_gnomonic_radius)

        # Hesse distance and alpha
        assert bands.hesse_distance.shape == hesse_distance.shape
        assert np.allclose(bands.hesse_distance, hesse_distance)
        assert bands.hesse_alpha.shape == hesse_alpha.shape
        assert np.allclose(bands.hesse_alpha, hesse_alpha, equal_nan=True)
Esempio n. 8
0
    def geometrical_simulation(
        self,
        reciprocal_lattice_point: Optional[ReciprocalLatticePoint] = None,
    ) -> GeometricalEBSDSimulation:
        """Project a set of center positions of Kikuchi bands on the
        detector, one set for each rotation of the unit cell.

        Parameters
        ----------
        reciprocal_lattice_point :
            Crystal planes to project onto the detector. If None, and
            the generator has a phase with a unit cell with a point
            group, a set of planes with minimum distance of 1 Å and
            their symmetrically equivalent planes are used.

        Returns
        -------
        GeometricalEBSDSimulation

        Examples
        --------
        >>> from diffsims.crystallography import ReciprocalLatticePoint
        >>> simgen
        EBSDSimulationGenerator (1,)
        EBSDDetector (60, 60), px_size 1 um, binning 1, tilt 0, pc
        (0.5, 0.5, 0.5)
        <name: . space group: None. point group: None. proper point
        group: None. color: tab:blue>
        Rotation (1,)
        >>> sim1 = simgen.geometrical_simulation()
        >>> sim1.bands.size
        94
        >>> rlp = ReciprocalLatticePoint(
        ...     phase=simgen.phase, hkl=[[1, 1, 1], [2, 0, 0]]
        ... )
        >>> sim2 = simgen.geometrical_simulation()
        >>> sim2.bands.size
        13
        """
        rlp = reciprocal_lattice_point
        if rlp is None and (hasattr(self.phase.point_group, "name") and
                            hasattr(self.phase.structure.lattice, "abcABG")):
            rlp = ReciprocalLatticePoint.from_min_dspacing(self.phase,
                                                           min_dspacing=1)
            rlp = rlp[rlp.allowed].symmetrise()
        elif rlp is None:
            raise ValueError("A ReciprocalLatticePoint object must be passed")
        self._rlp_phase_is_compatible(rlp)

        # Unit cell parameters (called more than once)
        phase = rlp.phase
        hkl = rlp._hkldata

        # Get Kikuchi band coordinates for all bands in all patterns
        # U_Kstar, transformation from detector frame D to reciprocal crystal
        # lattice frame Kstar
        # TODO: Possible bottleneck due to large dot products! Room for
        #  lots of improvements with dask.
        # Output shape is (3, n, 3) or (3, ny, nx, 3)
        det2recip = detector2reciprocal_lattice(
            sample_tilt=self.detector.sample_tilt,
            detector_tilt=self.detector.tilt,
            lattice=phase.structure.lattice,
            rotation=self.rotations,
        )
        # Output shape is (nhkl, n, 3) or (nhkl, ny, nx, 3)
        band_coordinates = np.tensordot(hkl, det2recip, axes=(1, 0))

        # Determine whether a band is visible in a pattern
        upper_hemisphere = band_coordinates[..., 2] > 0
        nav_dim = self.navigation_dimension
        navigation_axes = (1, 2)[:nav_dim]
        is_in_some_pattern = np.sum(upper_hemisphere,
                                    axis=navigation_axes) != 0

        # Get bands that were in some pattern and their coordinates in the
        # proper shape
        hkl = hkl[is_in_some_pattern, ...]
        hkl_in_pattern = upper_hemisphere[is_in_some_pattern, ...].T
        band_coordinates = np.moveaxis(band_coordinates[is_in_some_pattern],
                                       source=0,
                                       destination=nav_dim)

        # And store it all
        bands = KikuchiBand(
            phase=phase,
            hkl=hkl,
            hkl_detector=band_coordinates,
            in_pattern=hkl_in_pattern,
            gnomonic_radius=self.detector.r_max,
        )

        # Get zone axes coordinates
        # U_K, transformation from detector frame D to direct crystal lattice
        # frame K
        #        det2direct = detector2direct_lattice(
        #            sample_tilt=self.detector.sample_tilt,
        #            detector_tilt=self.detector.tilt,
        #            lattice=phase.structure.lattice,
        #            orientation=self.orientations,
        #        )
        #        hkl_transposed_upper = hkl_transposed[..., upper_hemisphere]
        #        axis_coordinates = det2direct.T.dot(hkl_transposed_upper).T
        #        zone_axes = ZoneAxis(
        #            phase=phase, hkl=upper_hkl, coordinates=axis_coordinates
        #        )

        return GeometricalEBSDSimulation(
            detector=self.detector,
            rotations=self.rotations,
            bands=bands,
            #            zone_axes=zone_axes,
        )