def calculate_profile_data(self, structure, reciprocal_radius=1.0, magnitude_tolerance=1e-5, minimum_intensity=1e-3): """ Calculates a one dimensional diffraction profile for a structure. Parameters ---------- structure : Structure The structure for which to calculate the diffraction profile. reciprocal_radius : float The maximum radius of the sphere of reciprocal space to sample, in reciprocal angstroms. magnitude_tolerance : float The minimum difference between diffraction magnitudes in reciprocal angstroms for two peaks to be consdiered different. minimum_intensity : float The minimum intensity required for a diffraction peak to be considered real. Deals with numerical precision issues. Returns ------- diffsims.ProfileSimulation The diffraction profile corresponding to this structure and experimental conditions. """ max_r = reciprocal_radius wavelength = self.wavelength scattering_params = self.scattering_params latt = structure.lattice is_hex = is_lattice_hexagonal(latt) coeffs, fcoords, occus, dwfactors = get_vectorized_list_for_atomic_scattering_factors( structure, {}, scattering_params=scattering_params) # Obtain crystallographic reciprocal lattice points within range recip_latt = latt.reciprocal() spot_indicies, _, spot_distances = get_points_in_sphere( recip_latt, reciprocal_radius) peaks = {} mask = np.logical_not((np.any(spot_indicies, axis=1) == 0)) for hkl, g_hkl in zip(spot_indicies[mask], spot_distances[mask]): # Force miller indices to be integers. hkl = [int(round(i)) for i in hkl] d_hkl = 1 / g_hkl # Bragg condition # theta = asin(wavelength * g_hkl / 2) # s = sin(theta) / wavelength = 1 / 2d = |ghkl| / 2 (d = # 1/|ghkl|) s = g_hkl / 2 # Store s^2 since we are using it a few times. s2 = s**2 # Vectorized computation of g.r for all fractional coords and # hkl. g_dot_r = np.dot(fcoords, np.transpose([hkl])).T[0] # Highly vectorized computation of atomic scattering factors. fs = np.sum(coeffs[:, :, 0] * np.exp(-coeffs[:, :, 1] * s2), axis=1) dw_correction = np.exp(-dwfactors * s2) # Structure factor = sum of atomic scattering factors (with # position factor exp(2j * pi * g.r and occupancies). # Vectorized computation. f_hkl = np.sum(fs * occus * np.exp(2j * np.pi * g_dot_r) * dw_correction) # Intensity for hkl is modulus square of structure factor. i_hkl = (f_hkl * f_hkl.conjugate()).real # two_theta = degrees(2 * theta) if is_hex: # Use Miller-Bravais indices for hexagonal lattices. hkl = (hkl[0], hkl[1], -hkl[0] - hkl[1], hkl[2]) peaks[g_hkl] = [i_hkl, [tuple(hkl)], d_hkl] # Scale intensities so that the max intensity is 100. max_intensity = max([v[0] for v in peaks.values()]) x = [] y = [] hkls = [] d_hkls = [] for k in sorted(peaks.keys()): v = peaks[k] fam = get_unique_families(v[1]) if v[0] / max_intensity * 100 > minimum_intensity: x.append(k) y.append(v[0]) hkls.append(fam) d_hkls.append(v[2]) y = y / max(y) * 100 return ProfileSimulation(x, y, hkls)
def test_get_unique_families(): hkls = ((0, 1, 1), (1, 1, 0)) unique_families = get_unique_families(hkls) assert unique_families == {(1, 1, 0): 2}