Пример #1
0
    def get_vector_library(self, reciprocal_radius):
        """Calculates a library of diffraction vectors and pairwise inter-vector
        angles for a library of crystal structures.

        Parameters
        ----------
        reciprocal_radius : float
            The maximum g-vector magnitude to be included in the library.

        Returns
        -------
        vector_library : :class:`DiffractionVectorLibrary`
            Mapping of phase identifier to a numpy array with entries in the
            form: [hkl1, hkl2, len1, len2, angle] ; lengths are in reciprocal
            Angstroms and angles are in radians.

        """
        # Define DiffractionVectorLibrary object to contain results
        vector_library = DiffractionVectorLibrary()
        # Get structures from structure library
        structure_library = self.structures.struct_lib
        # Iterate through phases in library.
        for phase_name in structure_library.keys():
            # Get diffpy.structure object associated with phase
            structure = structure_library[phase_name][0]
            # Get reciprocal lattice points within reciprocal_radius
            recip_latt = structure.lattice.reciprocal()
            indices, coordinates, distances = get_points_in_sphere(
                recip_latt, reciprocal_radius)

            # Iterate through all pairs calculating interplanar angle
            phase_vector_pairs = []
            for comb in itertools.combinations(np.arange(len(indices)), 2):
                i, j = comb[0], comb[1]
                # Specify hkls and lengths associated with the crystal structure.
                # TODO: This should be updated to reflect systematic absences
                if np.count_nonzero(coordinates[i]) == 0 or np.count_nonzero(
                        coordinates[j]) == 0:
                    continue  # Ignore combinations including [000]
                hkl1 = indices[i]
                hkl2 = indices[j]
                len1 = distances[i]
                len2 = distances[j]
                if len1 < len2:  # Keep the longest first
                    hkl1, hkl2 = hkl2, hkl1
                    len1, len2 = len1, len2
                angle = get_angle_cartesian(coordinates[i], coordinates[j])
                phase_vector_pairs.append(
                    np.array([hkl1, hkl2, len1, len2, angle]))
            vector_library[phase_name] = np.array(phase_vector_pairs)

        # Pass attributes to diffraction library from structure library.
        vector_library.identifiers = self.structures.identifiers
        vector_library.structures = self.structures.structures

        return vector_library
Пример #2
0
    def get_vector_library(self,
                           reciprocal_radius):
        """Calculates a library of diffraction vectors and pairwise inter-vector
        angles for a library of crystal structures.

        Parameters
        ----------
        reciprocal_radius : float
            The maximum g-vector magnitude to be included in the library.

        Returns
        -------
        vector_library : :class:`DiffractionVectorLibrary`
            Mapping of phase identifier to phase information in dictionary
            format.
        """
        # Define DiffractionVectorLibrary object to contain results
        vector_library = DiffractionVectorLibrary()
        # Get structures from structure library
        structure_library = self.structures.struct_lib
        # Iterate through phases in library.
        for phase_name in structure_library.keys():
            # Get diffpy.structure object associated with phase
            structure = structure_library[phase_name][0]
            # Get reciprocal lattice points within reciprocal_radius
            recip_latt = structure.lattice.reciprocal()
            miller_indices, coordinates, distances = get_points_in_sphere(
                recip_latt,
                reciprocal_radius)

            # Create pair_indices for selecting all point pair combinations
            num_indices = len(miller_indices)
            pair_a_indices, pair_b_indices = np.mgrid[:num_indices, :num_indices]

            # Only select one of the permutations and don't pair an index with
            # itself (select above diagonal)
            upper_indices = np.triu_indices(num_indices, 1)
            pair_a_indices = pair_a_indices[upper_indices].ravel()
            pair_b_indices = pair_b_indices[upper_indices].ravel()

            # Mask off origin (0, 0, 0)
            origin_index = num_indices // 2
            pair_a_indices = pair_a_indices[pair_a_indices != origin_index]
            pair_b_indices = pair_b_indices[pair_b_indices != origin_index]

            pair_indices = np.vstack([pair_a_indices, pair_b_indices])

            # Create library entries
            angles = get_angle_cartesian_vec(coordinates[pair_a_indices], coordinates[pair_b_indices])
            pair_distances = distances[pair_indices.T]
            # Ensure longest vector is first
            len_sort = np.fliplr(pair_distances.argsort(axis=1))
            # phase_index_pairs is a list of [hkl1, hkl2]
            phase_index_pairs = np.take_along_axis(miller_indices[pair_indices.T], len_sort[:, :, np.newaxis], axis=1)
            # phase_measurements is a list of [len1, len2, angle]
            phase_measurements = np.column_stack((np.take_along_axis(pair_distances, len_sort, axis=1), angles))

            # Only keep unique triplets
            unique_measurements, unique_measurement_indices = np.unique(phase_measurements, axis=0, return_index=True)
            vector_library[phase_name] = {
                'indices': phase_index_pairs[unique_measurement_indices],
                'measurements': unique_measurements
            }

        # Pass attributes to diffraction library from structure library.
        vector_library.identifiers = self.structures.identifiers
        vector_library.structures = self.structures.structures
        vector_library.reciprocal_radius = reciprocal_radius

        return vector_library
Пример #3
0
    def calculate_ed_data(self,
                          structure,
                          reciprocal_radius,
                          with_direct_beam=True):
        """Calculates the Electron Diffraction data for a structure.

        Parameters
        ----------
        structure : Structure
            The structure for which to derive the diffraction pattern. Note that
            the structure must be rotated to the appropriate orientation and
            that testing is conducted on unit cells (rather than supercells).
        reciprocal_radius : float
            The maximum radius of the sphere of reciprocal space to sample, in
            reciprocal angstroms.

        Returns
        -------
        pyxem.DiffractionSimulation
            The data associated with this structure and diffraction setup.

        """
        # Specify variables used in calculation
        wavelength = self.wavelength
        max_excitation_error = self.max_excitation_error
        debye_waller_factors = self.debye_waller_factors
        latt = structure.lattice
        scattering_params = self.scattering_params

        # Obtain crystallographic reciprocal lattice points within `max_r` and
        # g-vector magnitudes for intensity calculations.
        recip_latt = latt.reciprocal()
        spot_indicies, cartesian_coordinates, spot_distances = get_points_in_sphere(
            recip_latt, reciprocal_radius)

        # Identify points intersecting the Ewald sphere within maximum
        # excitation error and store the magnitude of their excitation error.
        r_sphere = 1 / wavelength
        r_spot = np.sqrt(np.sum(np.square(cartesian_coordinates[:, :2]), axis=1))
        z_sphere = -np.sqrt(r_sphere**2 - r_spot**2) + r_sphere
        proximity = np.absolute(z_sphere - cartesian_coordinates[:, 2])
        intersection = proximity < max_excitation_error
        # Mask parameters corresponding to excited reflections.
        intersection_coordinates = cartesian_coordinates[intersection]
        intersection_indices = spot_indicies[intersection]
        proximity = proximity[intersection]
        g_hkls = spot_distances[intersection]

        # Calculate diffracted intensities based on a kinematical model.
        intensities = get_kinematical_intensities(structure,
                                                  intersection_indices,
                                                  g_hkls,
                                                  proximity,
                                                  max_excitation_error,
                                                  debye_waller_factors,
                                                  scattering_params)

        # Threshold peaks included in simulation based on minimum intensity.
        peak_mask = intensities > 1e-20
        intensities = intensities[peak_mask]
        intersection_coordinates = intersection_coordinates[peak_mask]
        intersection_indices = intersection_indices[peak_mask]

        return DiffractionSimulation(coordinates=intersection_coordinates,
                                     indices=intersection_indices,
                                     intensities=intensities,
                                     with_direct_beam=with_direct_beam)
Пример #4
0
def test_get_points_in_sphere():
    latt = diffpy.structure.lattice.Lattice(0.5, 0.5, 0.5, 90, 90, 90)
    ind, cord, dist = get_points_in_sphere(latt, 0.6)
    assert len(ind) == len(cord)
    assert len(ind) == len(dist)
    assert len(dist) == 1 + 6
Пример #5
0
    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
        -------
        pyxem.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)