def test_multiplicity(self, nickel_phase, silicon_carbide_phase): assert np.allclose( ReciprocalLatticePoint.from_min_dspacing( phase=nickel_phase, min_dspacing=1 ).multiplicity, # fmt: off np.array([ 8, 24, 24, 24, 12, 24, 48, 48, 24, 24, 48, 24, 24, 24, 6, 8, 24, 24, 12, 24, 48, 24, 24, 24, 6, 8, 24, 12, 24, 24, 6, 8, 12, 6 ]) # fmt: on ) assert np.allclose( ReciprocalLatticePoint.from_min_dspacing( phase=silicon_carbide_phase, min_dspacing=1 ).multiplicity, # fmt: off np.array([ 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1 ]) # fmt: on )
def test_init_from_min_dspacing(self, ferrite_phase, min_dspacing, desired_size): assert ( ReciprocalLatticePoint.from_min_dspacing( phase=ferrite_phase, min_dspacing=min_dspacing ).size == desired_size )
def test_gspacing_dspacing_scattering_parameter(self, ferrite_phase): rlp = ReciprocalLatticePoint.from_min_dspacing( phase=ferrite_phase, min_dspacing=2 ) # fmt: off assert np.allclose( rlp.gspacing, np.array([ 1.2084778, 1.04657248, 0.98671799, 0.85452285, 0.78006907, 0.69771498, 0.604238, 0.493359, 0.34885749 ]) ) assert np.allclose( rlp.dspacing, np.array([ 0.82748727, 0.9555, 1.01346079, 1.17024372, 1.28193777, 1.43325, 1.65497455, 2.02692159, 2.8665 ]) ) assert np.allclose( rlp.scattering_parameter, np.array([ 0.6042389, 0.52328624, 0.493359, 0.42726142, 0.39003453, 0.34885749, 0.30211945, 0.2466795, 0.17442875 ]) )
def test_get_hkl(self, silicon_carbide_phase): rlp = ReciprocalLatticePoint.from_min_dspacing( silicon_carbide_phase, min_dspacing=3 ) assert np.allclose(rlp.h, [1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]) assert np.allclose(rlp.k, [1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0]) assert np.allclose(rlp.l, [4, 3, 2, 1, 0, 4, 3, 2, 0, 4, 3, 2])
def test_repr(self, ferrite_phase): rlp = ReciprocalLatticePoint.from_min_dspacing(ferrite_phase, min_dspacing=2) assert repr(rlp) == ( f"ReciprocalLatticePoint (9,)\n" f"Phase: ferrite (m-3m)\n" "[[2 2 2]\n [2 2 1]\n [2 2 0]\n [2 1 1]\n [2 1 0]\n [2 0 0]\n [1 1 1]\n" " [1 1 0]\n [1 0 0]]" )
def test_get_item(self, ferrite_phase): rlp = ReciprocalLatticePoint.from_min_dspacing( phase=ferrite_phase, min_dspacing=1.5 ) rlp.calculate_structure_factor() rlp.calculate_theta(voltage=20e3) assert rlp[0].size == 1 assert rlp[:2].size == 2 assert np.allclose(rlp[5:7].hkl.data, rlp.hkl[5:7].data) assert np.allclose(rlp[10:13].structure_factor, rlp.structure_factor[10:13]) assert np.allclose(rlp[20:23].theta, rlp.theta[20:23]) assert rlp.phase.space_group.number == rlp[0].phase.space_group.number assert rlp.phase.point_group.name == rlp[10:15].phase.point_group.name assert np.allclose( rlp.phase.structure.lattice.abcABG(), rlp[20:23].phase.structure.lattice.abcABG(), )
def test_allowed_raises(self, silicon_carbide_phase): with pytest.raises(NotImplementedError): _ = ReciprocalLatticePoint.from_min_dspacing( phase=silicon_carbide_phase, min_dspacing=1 ).allowed
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, )
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, )