Exemplo n.º 1
0
def get_tetrahedra_cross_section_weights(reciprocal_lattice, kpoints,
                                         tetrahedra, e21, e31, e41):
    # weight (b) defined by equation 3.4 in https://doi.org/10.1002/pssb.2220540211
    # this weight is not the Bloechl integrand but a scaling needed to obtain the
    # DOS directly from the tetrahedron cross section
    cross_section_weights = {}

    # volume is 6 * the volume of one tetrahedron
    volume = np.linalg.det(reciprocal_lattice) / len(kpoints)
    for spin, s_tetrahedra in tetrahedra.items():
        tetrahedra_kpoints = kpoints[s_tetrahedra]

        k1 = pbc_diff(tetrahedra_kpoints[:, :, 1], tetrahedra_kpoints[:, :, 0])
        k2 = pbc_diff(tetrahedra_kpoints[:, :, 2], tetrahedra_kpoints[:, :, 0])
        k3 = pbc_diff(tetrahedra_kpoints[:, :, 3], tetrahedra_kpoints[:, :, 0])

        k1_cart = np.dot(k1, reciprocal_lattice)
        k2_cart = np.dot(k2, reciprocal_lattice)
        k3_cart = np.dot(k3, reciprocal_lattice)

        contragradient = np.stack(
            [
                np.cross(k2_cart, k3_cart) / volume,
                np.cross(k3_cart, k1_cart) / volume,
                np.cross(k1_cart, k2_cart) / volume,
            ],
            axis=2,
        )

        energies = np.stack([e21[spin], e31[spin], e41[spin]], axis=2)
        b_vector = np.sum(contragradient * energies[..., None], axis=2)

        cross_section_weights[spin] = 1 / np.linalg.norm(b_vector, axis=2)

    return cross_section_weights
Exemplo n.º 2
0
    def get_overlap(self, spin, band_a, kpoint_a, band_b, kpoint_b):
        bands, kpoints, single_overlap = group_bands_and_kpoints(
            band_a, kpoint_a, band_b, kpoint_b)
        p = self.get_coefficients(spin, bands, kpoints)

        centers = self.band_centers[spin][band_a]
        center = centers[np.argmin(
            np.linalg.norm(pbc_diff(centers, kpoint_a), axis=1))]

        shift_a = pbc_diff(kpoint_a, center)
        shift_b = pbc_diff(kpoint_b, center)

        angles = cosine(shift_a, shift_b)
        angle_weights = np.abs(pbc_diff(kpoint_a, kpoint_b))
        angle_weights /= np.max(angle_weights, axis=1)[:, None]
        angle_weights[np.isnan(angle_weights)] = 0

        # weight the angle masks by the contribution of the transition in that direction
        # use the mask to get the angle scaling factor which gives how much the angle
        # is applied to each projection. I.e., if the scaling factor is 1 for a
        # particular projection, then the projection is scaled by 1 * the angle. If the
        # scaling factor is 0, the projection is scaled by 0 * the angle.
        # this allows us to make s orbitals immune to the cosine weight
        scaling_factor = self.rotation_mask[None] * angle_weights[..., None]

        prod = p[0] * p[1:]
        overlap = prod * (
            1 - scaling_factor) + prod * scaling_factor * angles[:, None]
        overlap = overlap.sum(axis=1)**2

        if single_overlap:
            return overlap[0]
        else:
            return overlap
Exemplo n.º 3
0
 def test_pbc_diff(self):
     self.assertArrayAlmostEqual(
         coord.pbc_diff([0.1, 0.1, 0.1], [0.3, 0.5, 0.9]),
         [-0.2, -0.4, 0.2])
     self.assertArrayAlmostEqual(
         coord.pbc_diff([0.9, 0.1, 1.01], [0.3, 0.5, 0.9]),
         [-0.4, -0.4, 0.11])
     self.assertArrayAlmostEqual(
         coord.pbc_diff([0.1, 0.6, 1.01], [0.6, 0.1, 0.9]),
         [-0.5, 0.5, 0.11])
     self.assertArrayAlmostEqual(
         coord.pbc_diff([100.1, 0.2, 0.3], [0123123.4, 0.5, 502312.6]),
         [-0.3, -0.3, -0.3])
Exemplo n.º 4
0
 def is_image(self, poly, tol):
     frac_diff = pbc_diff(poly.frac_coords, self.frac_coords)
     if not np.allclose(frac_diff, [0, 0, 0], atol=tol):
         return False
     to_frac = self.lattice.get_fractional_coords
     for c1 in self.polyhedron_coords:
         found = False
         for c2 in poly.polyhedron_coords:
             d = pbc_diff(to_frac(c1), to_frac(c2))
             if not np.allclose(d, [0, 0, 0], atol=tol):
                 found = True
                 break
         if not found:
             return False
     return True
Exemplo n.º 5
0
    def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol=1e-2):
        """
        Returns a list of unique symmetrically equivalent k-points.

        Args:
            kpoint (1x3 array): coordinate of the k-point
            cartesian (bool): kpoint is in cartesian or fractional coordinates
            tol (float): tolerance below which coordinates are considered equal

        Returns:
            ([1x3 array] or None): if structure is not available returns None
        """
        if not self.structure:
            return None
        sg = SpacegroupAnalyzer(self.structure)
        symmops = sg.get_point_group_operations(cartesian=cartesian)
        points = np.dot(kpoint, [m.rotation_matrix for m in symmops])
        rm_list = []
        # identify and remove duplicates from the list of equivalent k-points:
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol):
                    rm_list.append(i)
                    break
        return np.delete(points, rm_list, axis=0)
Exemplo n.º 6
0
    def get_sym_eq_kpoints(self, kpoint, cartesian=False, tol=1e-2):
        """
        Returns a list of unique symmetrically equivalent k-points.

        Args:
            kpoint (1x3 array): coordinate of the k-point
            cartesian (bool): kpoint is in cartesian or fractional coordinates
            tol (float): tolerance below which coordinates are considered equal

        Returns:
            ([1x3 array] or None): if structure is not available returns None
        """
        if not self.structure:
            return None
        sg = SpacegroupAnalyzer(self.structure)
        symmops = sg.get_point_group_operations(cartesian=cartesian)
        points = np.dot(kpoint, [m.rotation_matrix for m in symmops])
        rm_list = []
        # identify and remove duplicates from the list of equivalent k-points:
        for i in range(len(points) - 1):
            for j in range(i + 1, len(points)):
                if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol):
                    rm_list.append(i)
                    break
        return np.delete(points, rm_list, axis=0)
Exemplo n.º 7
0
def rmsd_pbc(file_path_1, file_path_2, original_def=True):
    """
    Calculate absolute root-mean-square diffence between two structures.
    No rotation nor recentering will be considered. Periodic boundary condition
    will be considered.
    """
    try:
        a = Structure.from_file(filename=file_path_1)
        b = Structure.from_file(filename=file_path_2)
    except Exception:
        sys.exit("File import failed.")

    # check if two structures are valid for compare
    natoms = check_validity(a, b)

    # get fractional coords of each structure
    # a_frac = [a[i].frac_coords for i in range(natoms)]
    # b_frac = [b[i].frac_coords for i in range(natoms)]
    a_frac = a.frac_coords
    b_frac = b.frac_coords

    # get frac_diff considering pbc (abs(diff) <= 0.5)
    frac_diff = pbc_diff(a_frac, b_frac)

    # convert to cartesian coords difference
    cart_diff = a.lattice.get_cartesian_coords(frac_diff)

    if original_def:
        # original definition of RMSD
        return np.sqrt(np.sum(cart_diff**2))
    else:
        # revised definition. The top 5 deviation is considered
        # aiming to better compare the difference of two similar structures
        return np.sum(np.sort(np.abs(cart_diff))[:5]) / 5
Exemplo n.º 8
0
 def should_stop(self, old_centroids, centroids, iterations):
     if iterations > self.max_iterations:
         warnings.warn("Max iterations %d reached!" % self.max_iterations)
         return True
     if old_centroids is None:
         return False
     for c1, c2 in zip(old_centroids, centroids):
         if not np.allclose(pbc_diff(c1, c2), [0, 0, 0]):
             return False
     return True
Exemplo n.º 9
0
    def get_overlap(self, spin, band_a, kpoint_a, band_b, kpoint_b):
        bands, kpoints, single_overlap = group_bands_and_kpoints(
            band_a, kpoint_a, band_b, kpoint_b)
        p = self.get_coefficients(spin, bands, kpoints)

        centers = self.band_centers[spin][band_a]
        center = centers[np.argmin(
            np.linalg.norm(pbc_diff(centers, kpoint_a), axis=1))]

        shift_a = pbc_diff(kpoint_a, center)
        shift_b = pbc_diff(kpoint_b, center)

        angles = cosine(shift_a, shift_b)
        prod = p[0] * p[1:]
        overlap = (prod * (1 - self.rotation_mask) +
                   prod * self.rotation_mask * angles[:, None])
        overlap = overlap.sum(axis=1)**2

        if single_overlap:
            return overlap[0]
        else:
            return overlap
Exemplo n.º 10
0
        def get_structures(vaspruns):
            for i, vr in enumerate(vaspruns):
                if i == 0:
                    step_skip = vr.ionic_step_skip or 1
                    final_structure = vr.initial_structure
                    temperature = vr.parameters["TEEND"]
                    time_step = vr.parameters["POTIM"]
                    yield step_skip, temperature, time_step
                # check that the runs are continuous
                fdist = pbc_diff(vr.initial_structure.frac_coords, final_structure.frac_coords)
                if np.any(fdist > 0.001):
                    raise ValueError("initial and final structures do not match.")
                final_structure = vr.final_structure

                assert (vr.ionic_step_skip or 1) == step_skip
                for s in vr.ionic_steps:
                    yield s["structure"]
Exemplo n.º 11
0
    def should_stop(self, old_centroids, centroids, iterations):
        """
        Check for stopping conditions.

        Args:
            old_centroids: List of old centroids
            centroids: List of centroids
            iterations: Number of iterations thus far.
        """
        if iterations > self.max_iterations:
            warnings.warn("Max iterations %d reached!" % self.max_iterations)
            return True
        if old_centroids is None:
            return False
        for c1, c2 in zip(old_centroids, centroids):
            if not np.allclose(pbc_diff(c1, c2), [0, 0, 0]):
                return False
        return True
Exemplo n.º 12
0
def get_equivalent_qpoint(qk, symops, qp, tol=1e-2):
    """Finds the closest phono3py qpoint to a phonopy qpoint.

    Arguments
    ---------

        qk : array-like
            all qpoints from the phono3py kappa file.
        symmops
            symmetry operations (e.g. from Pymatgen)
        qp : array-like
            single qpoint from the phonon dispersion.

        tol : float, optional
            tolerance. Default: 1e-2.

    Returns
    -------

        int
            nearest qpoint index.
    """

    from pymatgen.util.coord import pbc_diff

    # Equivalent qpoints

    points = np.dot(qp, [m.rotation_matrix for m in symops])

    # Remove duplicates

    rm_list = []
    for i in range(len(points) - 1):
        for j in range(i + 1, len(points)):
            if np.allclose(pbc_diff(points[i], points[j]), [0, 0, 0], tol):
                rm_list.append(i)
                break
    seq = np.delete(points, rm_list, axis=0)

    # Find nearest

    dists = [np.min(np.sum(np.power(k - seq, 2), axis=1)) for k in qk]

    return dists.index(np.min(dists))
Exemplo n.º 13
0
        def get_structures(vaspruns):
            for i, vr in enumerate(vaspruns):
                if i == 0:
                    step_skip = vr.ionic_step_skip or 1
                    final_structure = vr.initial_structure
                    temperature = vr.parameters['TEEND']
                    time_step = vr.parameters['POTIM']
                    yield step_skip, temperature, time_step
                # check that the runs are continuous
                fdist = pbc_diff(vr.initial_structure.frac_coords,
                                 final_structure.frac_coords)
                if np.any(fdist > 0.001):
                    raise ValueError('initial and final structures do not '
                                     'match.')
                final_structure = vr.final_structure

                assert (vr.ionic_step_skip or 1) == step_skip
                for s in vr.ionic_steps:
                    yield s['structure']
Exemplo n.º 14
0
    def test_cluster(self):
        lattice = Lattice.cubic(4)

        pts = []
        initial = [[0, 0, 0], [0.5, 0.5, 0.5], [0.25, 0.25, 0.25], [0.5, 0, 0]]
        for c in initial:
            for i in range(100):
                pts.append(np.array(c) + np.random.randn(3) * 0.01 +
                           np.random.randint(3))
        pts = np.array(pts)
        k = KmeansPBC(lattice)
        centroids, labels, ss = k.cluster(pts, 4)
        for c1 in centroids:
            found = False
            for c2 in centroids:
                if np.allclose(pbc_diff(c1, c2), [0, 0, 0], atol=0.1):
                    found = True
                    break
            self.assertTrue(found)
Exemplo n.º 15
0
    def is_periodic_image(self, other, tolerance=1e-8, check_lattice=True):
        """
        Returns True if sites are periodic images of each other.

        Args:
            other (PeriodicSite): Other site
            tolerance (float): Tolerance to compare fractional coordinates
            check_lattice (bool): Whether to check if the two sites have the
                same lattice.

        Returns:
            bool: True if sites are periodic images of each other.
        """
        if check_lattice and self.lattice != other.lattice:
            return False
        if self.species != other.species:
            return False

        frac_diff = pbc_diff(self.frac_coords, other.frac_coords)
        return np.allclose(frac_diff, [0, 0, 0], atol=tolerance)
Exemplo n.º 16
0
    def is_periodic_image(self, other, tolerance=1e-8, check_lattice=True):
        """
        Returns True if sites are periodic images of each other.

        Args:
            other (PeriodicSite): Other site
            tolerance (float): Tolerance to compare fractional coordinates
            check_lattice (bool): Whether to check if the two sites have the
                same lattice.

        Returns:
            bool: True if sites are periodic images of each other.
        """
        if check_lattice and self.lattice != other.lattice:
            return False
        if self.species != other.species:
            return False

        frac_diff = pbc_diff(self.frac_coords, other.frac_coords)
        return np.allclose(frac_diff, [0, 0, 0], atol=tolerance)
Exemplo n.º 17
0
    def calculate_rate(self, spin, b_idx, k_idx, energy_diff=None):
        rlat = self.amset_data.structure.lattice.reciprocal_lattice.matrix
        ir_kpoints_idx = self.amset_data.ir_kpoints_idx
        energy = self.amset_data.energies[spin][b_idx, ir_kpoints_idx][k_idx]

        if energy_diff:
            energy += energy_diff

        tbs = self.amset_data.tetrahedral_band_structure

        tet_dos, tet_mask, cs_weights, tet_contributions = tbs.get_tetrahedra_density_of_states(
            spin,
            energy,
            return_contributions=True,
            symmetry_reduce=False,
            # band_idx=b_idx,
        )

        if len(tet_dos) == 0:
            return 0

        # next, get k-point indices and band_indices
        property_mask, band_kpoint_mask, band_mask, kpoint_mask = tbs.get_masks(
            spin, tet_mask)

        k = self.amset_data.ir_kpoints[k_idx]
        k_primes = self.amset_data.kpoints[kpoint_mask]

        overlap = self.amset_data.overlap_calculator.get_overlap(
            spin, b_idx, k, band_mask, k_primes)

        # put overlap back in array with shape (nbands, nkpoints)
        all_overlap = np.zeros(self.amset_data.energies[spin].shape)
        all_overlap[band_kpoint_mask] = overlap

        # now select the properties at the tetrahedron vertices
        vert_overlap = all_overlap[property_mask]

        # get interpolated overlap and k-point at centre of tetrahedra cross sections
        tet_overlap = get_cross_section_values(vert_overlap,
                                               *tet_contributions)
        tetrahedra = tbs.tetrahedra[spin][tet_mask]

        # have to deal with the case where the tetrahedron cross section crosses the
        # zone boundary. This is a slight inaccuracy but we just treat the
        # cross section as if it is on one side of the boundary
        tet_kpoints = self.amset_data.kpoints[tetrahedra]
        base_kpoints = tet_kpoints[:, 0][:, None, :]
        k_diff = pbc_diff(tet_kpoints, base_kpoints) + pbc_diff(
            base_kpoints, k)
        k_diff = np.dot(k_diff, rlat)

        intersections = get_cross_section_values(k_diff,
                                                 *tet_contributions,
                                                 average=False)
        projected_intersections = get_projected_intersections(intersections)

        if energy_diff:
            f = np.zeros(self.amset_data.fermi_levels.shape)

            for n, t in np.ndindex(self.amset_data.fermi_levels.shape):
                f[n, t] = FD(
                    energy,
                    self.amset_data.fermi_levels[n, t],
                    self.amset_data.temperatures[t] * units.BOLTZMANN,
                )

            functions = np.array([
                m.factor(energy_diff <= 0, f)
                for m in self.inelastic_scatterers
            ])
        else:
            functions = np.array([m.factor() for m in self.elastic_scatterers])

        rates = np.array([
            integrate_function_over_cross_section(
                f,
                projected_intersections,
                *tet_contributions[0:3],
                return_shape=self.amset_data.fermi_levels.shape,
                cross_section_weights=cs_weights) for f in functions
        ])

        # sometimes the projected intersections can be nan when the density of states
        # contribution is infinitesimally small; this catches those errors
        rates[np.isnan(rates)] = 0

        rates /= self.amset_data.structure.lattice.reciprocal_lattice.volume
        rates *= tet_overlap

        return np.sum(rates, axis=-1)
Exemplo n.º 18
0
    def displacement_energies(self, structure, potential, displacement_energies_schema, supercell=(1, 1, 1), tollerance=0.1, max_displacement_energy=75, resolution=1, num_steps=1000, site_radius=0.5, timestep=0.001):
        """ Calculate displacement energy for each atom.

        Uses bisection method to determine displacement energy.
        """
        def ev2Aps(Z, energy):
            # sqrt((2 * energy[eV] [J/eV]) / (amu [g/mole] [kg/g])) * [m/s] [A/ps]
            return math.sqrt((2 * energy * 1.6021766208e-19) / (Z / (6.02214085e23 * 1e3))) * 1e-2

        if self.calculator_type == 'lammps':
            logger.warning('"lammps" calculator is depriciated use "lammps_cython" cannot promise working')
            relax_lammps_script = load_lammps_set('nve')
            relax_lammps_script['thermo'] = []
            relax_lammps_script
            relax_lammps_script['timestep'] = timestep # fs
            relax_lammps_script['run'] = num_steps
            kwargs = {'lammps_set': relax_lammps_script}
        elif self.calculator_type == 'lammps_cython':
            kwargs = {'lammps_additional_commands': [
                'timestep %f' % timestep,
                'velocity all zero linear',
                'fix 1 all nve',
                'run %d' % num_steps
            ]}

        energies = {}
        displacement_energies_schema = displacement_energies_schema.copy()
        for displacement_energy_name, d in displacement_energies_schema.items():
            base_structure = structure.copy()
            v = base_structure.lattice.get_cartesian_coords(d['direction'])
            cart_coords = base_structure.lattice.get_cartesian_coords(d['position'])
            base_structure = base_structure * supercell
            site = base_structure.get_sites_in_sphere(cart_coords, tollerance)[0][0]
            original_positions = base_structure.cart_coords
            original_frac_positions = base_structure.lattice.get_fractional_coords(original_positions)
            index = base_structure.index(site)

            min_energy, max_energy = 0.0, max_displacement_energy
            guess_energy = None
            while abs(max_energy - min_energy) > resolution:
                guess_energy = (max_energy - min_energy) / 2 + min_energy
                velocity = (v / np.linalg.norm(v)) * ev2Aps(Element(d['element']).atomic_mass, guess_energy)
                velocities = np.zeros((len(base_structure), 3))
                velocities[index] = velocity
                base_structure.add_site_property('velocities', velocities)

                async def calculate():
                    future = await self.calculator.submit(
                        base_structure, potential,
                        properties={'positions', 'initial_positions'},
                        **kwargs)
                    await future
                    return future.result()

                print('starting calculation (displacement energy): %s ion %s velocity: %f [eV] %f [A/ps]' % (displacement_energy_name, d['element'], guess_energy, ev2Aps(Element(d['element']).atomic_mass, guess_energy)))
                result = self._run_async_func(calculate())
                initial_frac_positions = base_structure.lattice.get_fractional_coords(result['results']['initial_positions'])
                final_frac_positions = base_structure.lattice.get_fractional_coords(result['results']['positions'])
                displacements = np.linalg.norm(
                    base_structure.lattice.get_cartesian_coords(
                        pbc_diff(final_frac_positions, initial_frac_positions)), axis=1)
                is_original_state = np.all(displacements < site_radius)
                print('finished calculation (displacement energy): %s resulted in ground_state (%s) max displacment %f [A] median %f [A] min %f [A]' % (displacement_energy_name, is_original_state, np.max(displacements), np.median(displacements), np.min(displacements)))
                if is_original_state:
                    min_energy = guess_energy
                else:
                    max_energy = guess_energy

            energies[displacement_energy_name] = guess_energy
        return energies
Exemplo n.º 19
0
    def calculate_rate(self, spin, b_idx, k_idx, energy_diff=None):
        rlat = self.amset_data.structure.lattice.reciprocal_lattice.matrix
        ir_kpoints_idx = self.amset_data.ir_kpoints_idx
        energy = self.amset_data.energies[spin][b_idx, ir_kpoints_idx][k_idx]

        if energy_diff:
            energy += energy_diff

        tbs = self.amset_data.tetrahedral_band_structure

        (
            tet_dos,
            tet_mask,
            cs_weights,
            tet_contributions,
        ) = tbs.get_tetrahedra_density_of_states(
            spin,
            energy,
            return_contributions=True,
            symmetry_reduce=False,
            # band_idx=b_idx,  # turn this on to disable interband scattering
        )

        if len(tet_dos) == 0:
            return 0

        # next, get k-point indices and band_indices
        # property_mask, band_kpoint_mask, band_mask, kpoint_mask = tbs.get_masks(
        #     spin, tet_mask
        # )

        k = self.amset_data.ir_kpoints[k_idx]
        # k_primes = self.amset_data.kpoints[kpoint_mask]

        # overlap = self.amset_data.overlap_calculator.get_overlap(
        #     spin, b_idx, k, band_mask, k_primes
        # )
        #
        # # put overlap back in array with shape (nbands, nkpoints)
        # all_overlap = np.zeros(self.amset_data.energies[spin].shape)
        # all_overlap[band_kpoint_mask] = overlap
        #
        # # now select the properties at the tetrahedron vertices
        # vert_overlap = all_overlap[property_mask]
        #
        # # get interpolated overlap at centre of tetrahedra cross sections
        # tet_overlap = get_cross_section_values(vert_overlap, *tet_contributions)
        tetrahedra = tbs.tetrahedra[spin][tet_mask]

        # have to deal with the case where the tetrahedron cross section crosses the
        # zone boundary. This is a slight inaccuracy but we just treat the
        # cross section as if it is on one side of the boundary
        tet_kpoints = self.amset_data.kpoints[tetrahedra]
        base_kpoints = tet_kpoints[:, 0][:, None, :]
        k_diff = pbc_diff(tet_kpoints, base_kpoints) + pbc_diff(
            base_kpoints, k)

        k_diff = np.dot(k_diff, rlat)
        intersections = get_cross_section_values(k_diff,
                                                 *tet_contributions,
                                                 average=False)
        projected_intersections, basis = get_projected_intersections(
            intersections)

        k_spacing = np.linalg.norm(
            np.dot(rlat, 1 / self.amset_data.kpoint_mesh))
        qpoints, weights, mapping = get_fine_mesh_qpoints(
            projected_intersections,
            basis,
            *tet_contributions[0:3],
            high_tol=k_spacing * 0.5,
            med_tol=k_spacing * 2,
            cross_section_weights=cs_weights)
        qpoint_norm_sq = np.sum(qpoints**2, axis=-1)

        k_primes = np.dot(qpoints, np.linalg.inv(rlat)) + k
        k_primes = kpoints_to_first_bz(k_primes)
        if energy_diff:
            fd = _get_fd(energy, self.amset_data)
            emission = energy_diff <= 0
            rates = [
                s.factor(qpoint_norm_sq, emission, fd)
                for s in self.inelastic_scatterers
            ]
            mrta_factor = 1
        else:
            mrta_factor = self.amset_data.mrta_calculator.get_mrta_factor(
                spin, b_idx, k, tet_mask[0][mapping], k_primes)
            rates = [s.factor(qpoint_norm_sq) for s in self.elastic_scatterers]

        rates = np.array(rates)

        # sometimes the projected intersections can be nan when the density of states
        # contribution is infinitesimally small; this catches those errors
        rates[np.isnan(rates)] = 0

        overlap = self.amset_data.overlap_calculator.get_overlap(
            spin, b_idx, k, tet_mask[0][mapping], k_primes)

        rates /= self.amset_data.structure.lattice.reciprocal_lattice.volume
        rates *= overlap * weights * mrta_factor

        return np.sum(rates, axis=-1)
Exemplo n.º 20
0
    def calculate_rate(self, spin, b_idx, k_idx, energy_diff=None):
        rlat = self.amset_data.structure.lattice.reciprocal_lattice.matrix
        energy = self.amset_data.energies[spin][b_idx, k_idx]
        velocity = self.amset_data.velocities[spin][b_idx, k_idx]

        if energy_diff:
            energy += energy_diff

        tbs = self.amset_data.tetrahedral_band_structure

        (
            tet_dos,
            tet_mask,
            cs_weights,
            tet_contributions,
        ) = tbs.get_tetrahedra_density_of_states(
            spin,
            energy,
            return_contributions=True,
            symmetry_reduce=False,
            # band_idx=b_idx,  # turn this on to disable interband scattering
        )

        if len(tet_dos) == 0:
            return 0

        # next, get k-point indices and band_indices
        property_mask, band_kpoint_mask, band_mask, kpoint_mask = tbs.get_masks(
            spin, tet_mask)
        k = self.amset_data.kpoints[k_idx]
        k_primes = self.amset_data.kpoints[kpoint_mask]

        if self.cache_wavefunction:
            # use cached coefficients to calculate the overlap on the fine mesh
            # tetrahedron vertices
            p1 = self._coeffs[spin][self._coeffs_mapping[spin][b_idx, k_idx]]
            p2 = self._coeffs[spin][self._coeffs_mapping[spin][band_mask,
                                                               kpoint_mask]]
            overlap = get_overlap(p1, p2)
        else:
            overlap = self.amset_data.overlap_calculator.get_overlap(
                spin, b_idx, k, band_mask, k_primes)

        # put overlap back in array with shape (nbands, nkpoints)
        all_overlap = np.zeros(self.amset_data.energies[spin].shape)
        all_overlap[band_kpoint_mask] = overlap

        # now select the properties at the tetrahedron vertices
        vert_overlap = all_overlap[property_mask]

        # get interpolated overlap at centre of tetrahedra cross sections
        tet_overlap = get_cross_section_values(vert_overlap,
                                               *tet_contributions)
        tetrahedra = tbs.tetrahedra[spin][tet_mask]

        # have to deal with the case where the tetrahedron cross section crosses the
        # zone boundary. This is a slight inaccuracy but we just treat the
        # cross section as if it is on one side of the boundary
        tet_kpoints = self.amset_data.kpoints[tetrahedra]
        base_kpoints = tet_kpoints[:, 0][:, None, :]
        k_diff = pbc_diff(tet_kpoints, base_kpoints) + pbc_diff(
            base_kpoints, k)

        # project the tetrahedron cross sections onto 2D surfaces in either a triangle
        # or quadrilateral
        k_diff = np.dot(k_diff, rlat)
        intersections = get_cross_section_values(k_diff,
                                                 *tet_contributions,
                                                 average=False)
        projected_intersections, basis = get_projected_intersections(
            intersections)

        k_spacing = np.linalg.norm(
            np.dot(rlat, 1 / self.amset_data.kpoint_mesh))
        qpoints, weights, mapping = get_fine_mesh_qpoints(
            projected_intersections,
            basis,
            *tet_contributions[0:3],
            high_tol=k_spacing * 0.5,
            med_tol=k_spacing * 2,
            cross_section_weights=cs_weights,
        )
        qpoint_norm_sq = np.sum(qpoints**2, axis=-1)

        k_primes = np.dot(qpoints, np.linalg.inv(rlat)) + k
        k_primes = kpoints_to_first_bz(k_primes)

        # unit q in reciprocal cartesian coordinates
        unit_q = qpoints / np.sqrt(qpoint_norm_sq)[:, None]
        if energy_diff:
            e_fd = _get_fd(energy, self.amset_data)
            emission = energy_diff <= 0
            rates = [
                s.factor(unit_q, qpoint_norm_sq, emission, e_fd)
                for s in self.inelastic_scatterers
            ]
            mrta_factor = 1
        else:
            mrta_factor = self.amset_data.mrta_calculator.get_mrta_factor(
                spin, b_idx, k, tet_mask[0][mapping], k_primes)
            rates = [
                s.factor(unit_q, qpoint_norm_sq, spin, b_idx, k, velocity)
                for s in self.elastic_scatterers
            ]

        rates = np.array(rates)
        rates /= self.amset_data.structure.lattice.reciprocal_lattice.volume
        rates *= tet_overlap[mapping] * weights * mrta_factor

        # this is too expensive vs tetrahedron integration and doesn't add much more
        # accuracy; could offer this as an option
        # overlap = self.amset_data.overlap_calculator.get_overlap(
        #     spin, b_idx, k, tet_mask[0][mapping], k_primes
        # )
        # rates *= overlap * weights * mrta_factor

        # sometimes the projected intersections can be nan when the density of states
        # contribution is infinitesimally small; this catches those errors
        rates[np.isnan(rates)] = 0

        return np.sum(rates, axis=-1)
Exemplo n.º 21
0
    def get_overlap(self, spin, band_a, kpoint_a, band_b, kpoint_b):
        # k-points should be in fractional
        kpoint_a = np.asarray(kpoint_a)
        kpoint_b = np.asarray(kpoint_b)

        v1 = np.array([[band_a] + kpoint_a.tolist()])

        single_overlap = False
        if isinstance(band_b, numeric_types):
            # only one band index given

            if len(kpoint_b.shape) > 1:
                # multiple k-point indices given
                band_b = np.array([band_b] * len(kpoint_b))

            else:
                band_b = np.array([band_b])
                kpoint_b = [kpoint_b]
                single_overlap = True

        else:
            band_b = np.asarray(band_b)

        centers = self.band_centers[spin][band_a]
        center = centers[np.argmin(
            np.linalg.norm(pbc_diff(centers, kpoint_a), axis=1))]

        shift_a = pbc_diff(kpoint_a, center)
        shift_b = pbc_diff(kpoint_b, center)

        angles = cosine(shift_a, shift_b)
        angle_weights = np.abs(pbc_diff(kpoint_a, kpoint_b))
        angle_weights /= np.max(angle_weights, axis=1)[:, None]
        angle_weights[np.isnan(angle_weights)] = 0

        # v2 now has shape of (nkpoints_b, 4)
        v2 = np.concatenate([band_b[:, None], kpoint_b], axis=1)

        # get a big array of all the k-points to interpolate
        all_v = np.vstack([v1, v2])

        # get the interpolate projections for the k-points; p1 is the projections for
        # kpoint_a, p2 is a list of projections for the kpoint_b
        p1, *p2 = self.interpolators[spin](all_v)

        # weight the angle masks by the contribution of the transition in that direction
        weighted_mask = self.rotation_masks[None, ...] * angle_weights[...,
                                                                       None]

        # use the mask to get the angle scaling factor which gives how much the angle
        # is applied to each projection. I.e., if the scaling factor is 1 for a
        # particular projection, then the projection is scaled by 1 * the angle. If the
        # scaling factor is 0.5, the projection is scaled by 0.5 * the angle.
        # this allows us to only weight specific orbitals
        scaling_factor = np.max(weighted_mask, axis=1)

        p_product = p1 * p2

        overlap = np.sum(
            p_product * (1 - scaling_factor) +
            p_product * scaling_factor * angles[:, None],
            axis=1,
        )
        # overlap = np.ones_like(overlap)

        if single_overlap:
            return overlap[0]**2
        else:
            return overlap**2
Exemplo n.º 22
0
    def displacement_energies(self, structure, potential, displacement_energies_schema, supercell=(1, 1, 1), tollerance=0.1, max_displacement_energy=75, resolution=1, num_steps=1000, site_radius=0.5, timestep=0.001):
        """ Calculate displacement energy for each atom.

        Uses bisection method to determine displacement energy.
        """
        def ev2Aps(Z, energy):
            # sqrt((2 * energy[eV] [J/eV]) / (amu [g/mole] [kg/g])) * [m/s] [A/ps]
            return math.sqrt((2 * energy * 1.6021766208e-19) / (Z / (6.02214085e23 * 1e3))) * 1e-2

        if self.calculator_type == 'lammps':
            logger.warning('"lammps" calculator is depriciated use "lammps_cython" cannot promise working')
            relax_lammps_script = load_lammps_set('nve')
            relax_lammps_script['thermo'] = []
            relax_lammps_script
            relax_lammps_script['timestep'] = timestep # fs
            relax_lammps_script['run'] = num_steps
            kwargs = {'lammps_set': relax_lammps_script}
        elif self.calculator_type == 'lammps_cython':
            kwargs = {'lammps_additional_commands': [
                'timestep %f' % timestep,
                'velocity all zero linear',
                'fix 1 all nve',
                'run %d' % num_steps
            ]}

        energies = {}
        displacement_energies_schema = displacement_energies_schema.copy()
        for displacement_energy_name, d in displacement_energies_schema.items():
            base_structure = structure.copy()
            v = base_structure.lattice.get_cartesian_coords(d['direction'])
            cart_coords = base_structure.lattice.get_cartesian_coords(d['position'])
            base_structure = base_structure * supercell
            site = base_structure.get_sites_in_sphere(cart_coords, tollerance)[0][0]
            original_positions = base_structure.cart_coords
            original_frac_positions = base_structure.lattice.get_fractional_coords(original_positions)
            index = base_structure.index(site)

            min_energy, max_energy = 0.0, max_displacement_energy
            guess_energy = None
            while abs(max_energy - min_energy) > resolution:
                guess_energy = (max_energy - min_energy) / 2 + min_energy
                velocity = (v / np.linalg.norm(v)) * ev2Aps(Element(d['element']).atomic_mass, guess_energy)
                velocities = np.zeros((len(base_structure), 3))
                velocities[index] = velocity
                base_structure.add_site_property('velocities', velocities)

                async def calculate():
                    future = await self.calculator.submit(
                        base_structure, potential,
                        properties={'positions', 'initial_positions'},
                        **kwargs)
                    await future
                    return future.result()

                print('starting calculation (displacement energy): %s ion %s velocity: %f [eV] %f [A/ps]' % (displacement_energy_name, d['element'], guess_energy, ev2Aps(Element(d['element']).atomic_mass, guess_energy)))
                result = self._run_async_func(calculate())
                initial_frac_positions = base_structure.lattice.get_fractional_coords(result['results']['initial_positions'])
                final_frac_positions = base_structure.lattice.get_fractional_coords(result['results']['positions'])
                displacements = np.linalg.norm(
                    base_structure.lattice.get_cartesian_coords(
                        pbc_diff(final_frac_positions, initial_frac_positions)), axis=1)
                is_original_state = np.all(displacements < site_radius)
                print('finished calculation (displacement energy): %s resulted in ground_state (%s) max displacment %f [A] median %f [A] min %f [A]' % (displacement_energy_name, is_original_state, np.max(displacements), np.median(displacements), np.min(displacements)))
                if is_original_state:
                    min_energy = guess_energy
                else:
                    max_energy = guess_energy

            energies[displacement_energy_name] = guess_energy
        return energies
Exemplo n.º 23
0
def get_ir_band_rates(spin, b_idx, scatterers, ediff, gauss_width, s, k_idx,
                      k_p_idx, kpoints, kpoint_norms, kpoint_weights, a_factor,
                      c_factor, reciprocal_lattice_matrix, ir_kpoints_idx,
                      grouped_ir_to_full, g=None, emission=False,
                      calculate_out_rate=True,
                      calculate_in_rate=True):
    from numpy.core.umath_tests import inner1d

    # k_idx and k_p_idx are currently their reduced form. E.g., span 0 to
    # n_ir_kpoints-1; find actual columns of k_p_idx in the full Brillouin zone
    # by lookup
    full_k_p_idx_grouped = grouped_ir_to_full[k_p_idx]

    # get the reduced k_idx including duplicate k_idx for the full k_prime
    repeated_k_idx = np.repeat(k_idx, [len(g) for g in full_k_p_idx_grouped])
    expand_k_idx = np.repeat(np.arange(
        len(k_idx)), [len(g) for g in full_k_p_idx_grouped])

    # flatten the list of mapped columns
    full_k_p_idx = np.concatenate(full_k_p_idx_grouped)

    # get the indices of the k_idx in the full Brillouin zone
    full_k_idx = ir_kpoints_idx[repeated_k_idx]

    mask = full_k_idx != full_k_p_idx
    full_k_idx = full_k_idx[mask]
    full_k_p_idx = full_k_p_idx[mask]
    expand_k_idx = expand_k_idx[mask]

    k_dot = inner1d(kpoints[full_k_idx], kpoints[full_k_p_idx])
    k_angles = k_dot / (kpoint_norms[full_k_idx] * kpoint_norms[full_k_p_idx])
    k_angles[np.isnan(k_angles)] = 1.

    a_vals = a_factor[k_idx] * a_factor[k_p_idx]
    c_vals = c_factor[k_idx] * c_factor[k_p_idx]

    overlap = (a_vals[expand_k_idx] + c_vals[expand_k_idx] * k_angles) ** 2
    weighted_overlap = (w0gauss(ediff / gauss_width)[expand_k_idx] * overlap *
                        kpoint_weights[full_k_p_idx])

    # norm of k difference squared in 1/nm
    k_diff_sq = np.linalg.norm(np.dot(
        pbc_diff(kpoints[full_k_idx], kpoints[full_k_p_idx]),
        reciprocal_lattice_matrix) / 0.1, axis=1) ** 2

    if isinstance(scatterers[0], AbstractElasticScattering):
        # factors has shape: (n_scatterers, n_doping, n_temperatures, n_kpts)
        factors = np.array([m.factor(k_diff_sq) for m in scatterers])
    else:
        # factors has shape: (n_scatterers, n_doping, n_temperatures, n_kpts)
        # first calculate scattering in rate
        to_stack = []
        if calculate_in_rate:
            in_factors = np.array([
                m.factor(spin, b_idx, full_k_idx, k_diff_sq, not emission)
                for m in scatterers])
            in_factors *= g[None, :, :, full_k_p_idx] * k_angles[None, None,
                                                                 None, :]
            to_stack.append(in_factors)

        if calculate_out_rate:
            out_factors = np.array([
                m.factor(spin, b_idx, full_k_p_idx, k_diff_sq, emission)
                for m in scatterers])
            to_stack.append(out_factors)

        factors = np.vstack(to_stack)

    rates = weighted_overlap * factors

    repeated_k_idx = repeated_k_idx[mask] - s.start
    unique_rows = np.unique(repeated_k_idx)

    # band rates has shape (n_scatterers, n_doping, n_temperatures, n_kpoints)
    band_rates = np.zeros((list(factors.shape[:-1]) + [s.stop - s.start]))

    # could vectorize this using numpy apply along axis if need be
    for s, n, t in np.ndindex(band_rates.shape[:-1]):
        band_rates[s, n, t, unique_rows] = np.bincount(
            repeated_k_idx, weights=rates[s, n, t])[unique_rows]

    return band_rates