Ejemplo n.º 1
0
def _interpolate_zero_rates(
    rates,
    kpoints,
    masks: Optional = None,
    progress_bar: bool = defaults["print_log"],
):
    # loop over all scattering types, doping, temps, and bands and interpolate
    # zero scattering rates based on the nearest k-point
    logger.info("Interpolating missing scattering rates")
    n_rates = sum([np.product(rates[spin].shape[:-1]) for spin in rates])
    if progress_bar:
        pbar = get_progress_bar(total=n_rates, desc="progress")
    else:
        pbar = None

    t0 = time.perf_counter()
    k_idx = np.arange(len(kpoints))
    for spin in rates:
        for s, d, t, b in np.ndindex(rates[spin].shape[:-1]):

            if masks is not None:
                mask = np.invert(masks[spin][s, d, t, b])
            else:
                mask = [True] * len(rates[spin][s, d, t, b])

            non_zero_rates = rates[spin][s, d, t, b, mask] > 1e7
            # non_zero_rates = rates[spin][s, d, t, b, mask] != 0
            zero_rate_idx = k_idx[mask][~non_zero_rates]
            non_zero_rate_idx = k_idx[mask][non_zero_rates]

            if not np.any(non_zero_rates):
                # all scattering rates are zero so cannot interpolate
                # generally this means the scattering prefactor is zero. E.g.
                # for POP when studying non polar materials
                rates[spin][s, d, t, b, mask] += small_val

            elif np.sum(non_zero_rates) != np.sum(mask):
                # electronic_structure seems to work best when all the kpoints are +ve
                # therefore add 0.5
                # Todo: Use cartesian coordinates (will be more robust to
                #  oddly shaped cells)
                rates[spin][s, d, t, b, zero_rate_idx] = griddata(
                    points=kpoints[non_zero_rate_idx] + 0.5,
                    values=rates[spin][s, d, t, b, non_zero_rate_idx],
                    xi=kpoints[zero_rate_idx] + 0.5,
                    method="nearest",
                )
                # rates[spin][s, d, t, b, zero_rate_idx] = 1e15

            if pbar is not None:
                pbar.update()

    if pbar is not None:
        pbar.close()
    log_time_taken(t0)

    return rates
Ejemplo n.º 2
0
def _calculate_transport_properties(amset_data,
                                    progress_bar: bool = defaults["print_log"]
                                    ):
    n_t_size = (len(amset_data.doping), len(amset_data.temperatures))

    sigma = np.zeros(n_t_size + (3, 3))
    seebeck = np.zeros(n_t_size + (3, 3))
    kappa = np.zeros(n_t_size + (3, 3))
    volume = amset_data.structure.volume

    epsilon, dos = amset_data.tetrahedral_band_structure.get_density_of_states(
        amset_data.dos.energies, sum_spins=True, use_cached_weights=True)

    iterable = list(np.ndindex(n_t_size))
    if progress_bar:
        pbar = get_progress_bar(iterable=iterable, desc="transport")
    else:
        pbar = iterable

    # solve sigma, seebeck, kappa and hall using information from all bands
    for n, t in pbar:
        lifetimes = {
            s: 1 / np.sum(amset_data.scattering_rates[s][:, n, t], axis=0)
            for s in amset_data.spins
        }

        # Nones are required as BoltzTraP2 expects the Fermi and temp as arrays
        fermi = amset_data.fermi_levels[n, t][None]
        temp = amset_data.temperatures[t][None]

        # obtain the Fermi integrals
        vvdos = get_transport_dos(
            amset_data.tetrahedral_band_structure,
            amset_data.velocities_product,
            lifetimes,
            amset_data.dos.energies,
        )

        _, l0, l1, l2, lm11 = fermiintegrals(
            epsilon,
            dos,
            vvdos,
            mur=fermi,
            Tr=temp,
            dosweight=amset_data.dos.dos_weight)

        # Compute the Onsager coefficients from Fermi integrals
        # Don't store the Hall coefficient as we don't have the curvature
        # information.
        sigma[n, t], seebeck[n, t], kappa[n, t], _ = calc_Onsager_coefficients(
            l0, l1, l2, fermi, temp, volume)

    # convert seebeck to µV/K
    seebeck *= 1e6

    return sigma, seebeck, kappa
Ejemplo n.º 3
0
def desymmetrize_deformation_potentials(deformation_potentials,
                                        structure,
                                        rotations,
                                        op_mapping,
                                        kp_mapping,
                                        pbar=True):
    logger.info("Desymmetrizing deformation potentials")
    t0 = time.perf_counter()

    rlat = structure.lattice.reciprocal_lattice.matrix
    sims = np.array([similarity_transformation(rlat, r) for r in rotations])
    inv_sims = np.array([np.linalg.inv(s) for s in sims])

    sims = sims[op_mapping]
    inv_sims = inv_sims[op_mapping]

    all_deformation_potentials = {}
    for spin, spin_deformation_potentials in deformation_potentials.items():
        all_deformation_potentials[spin] = np.zeros(
            (len(spin_deformation_potentials), len(sims), 3, 3))

        state_idxs = list(
            np.ndindex((len(spin_deformation_potentials), len(sims))))
        if pbar:
            state_idxs = get_progress_bar(state_idxs, desc="progress")

        for b_idx, k_idx in state_idxs:
            map_idx = kp_mapping[k_idx]

            sim = sims[k_idx]
            inv_sim = inv_sims[k_idx]

            inner = np.dot(sim, spin_deformation_potentials[b_idx, map_idx])
            rot_deform = np.abs(np.dot(inner, inv_sim))

            # inner = np.dot(spin_deformation_potentials[b_idx, map_idx], inv_sim)
            # rot_deform = np.abs(np.dot(sim, inner))
            # inner = np.dot(spin_deformation_potentials[b_idx, map_idx], sim)
            # rot_deform = np.abs(np.dot(inv_sim, inner))

            all_deformation_potentials[spin][b_idx, k_idx] = rot_deform

    log_time_taken(t0)
    return all_deformation_potentials
Ejemplo n.º 4
0
def _calculate_mobility(
    amset_data: AmsetData,
    rate_idx: Union[int, List[int], np.ndarray],
    pbar_label: str = "mobility",
):
    if isinstance(rate_idx, int):
        rate_idx = [rate_idx]

    volume = amset_data.structure.volume
    mobility = np.zeros(amset_data.fermi_levels.shape + (3, 3))

    epsilon, dos = amset_data.tetrahedral_band_structure.get_density_of_states(
        amset_data.dos.energies, sum_spins=True, use_cached_weights=True)

    pbar = get_progress_bar(iterable=list(
        np.ndindex(amset_data.fermi_levels.shape)),
                            desc=pbar_label)
    for n, t in pbar:
        br = {
            s: np.arange(len(amset_data.energies[s]))
            for s in amset_data.spins
        }
        cb_idx = {s: amset_data.vb_idx[s] + 1 for s in amset_data.spins}

        if amset_data.doping[n] < 0:
            band_idx = {s: br[s][cb_idx[s]:] for s in amset_data.spins}
        else:
            band_idx = {s: br[s][:cb_idx[s]] for s in amset_data.spins}

        lifetimes = {
            s:
            1 / np.sum(amset_data.scattering_rates[s][rate_idx, n, t], axis=0)
            for s in amset_data.spins
        }

        # Nones are required as BoltzTraP2 expects the Fermi and temp as arrays
        fermi = amset_data.fermi_levels[n, t][None]
        temp = amset_data.temperatures[t][None]

        # obtain the Fermi integrals for the temperature and doping
        vvdos = get_transport_dos(
            amset_data.tetrahedral_band_structure,
            amset_data.velocities_product,
            lifetimes,
            amset_data.dos.energies,
            band_idx=band_idx,
        )

        c, l0, l1, l2, lm11 = fermiintegrals(
            epsilon,
            dos,
            vvdos,
            mur=fermi,
            Tr=temp,
            dosweight=amset_data.dos.dos_weight)

        # Compute the Onsager coefficients from Fermi integrals
        sigma, _, _, _ = calc_Onsager_coefficients(l0, l1, l2, fermi, temp,
                                                   volume)

        if amset_data.doping[n] < 0:
            carrier_conc = amset_data.electron_conc[n, t]
        else:
            carrier_conc = amset_data.hole_conc[n, t]

        # don't use c as we don't use the correct DOS each time
        # c = -c[0, ...] / (volume / (Meter / 100.)**3)

        # convert mobility to cm^2/V.s
        uc = 0.01 / (e * carrier_conc * (1 / bohr_to_cm)**3)
        mobility[n, t] = sigma[0, ...] * uc

    return mobility
Ejemplo n.º 5
0
    def calculate_band_rates(self, spin: Spin, b_idx: int):
        vol = self.amset_data.structure.lattice.reciprocal_lattice.volume
        conversion = vol / (4 * np.pi**2)
        kpoints_idx = self.amset_data.ir_kpoints_idx
        nkpoints = len(kpoints_idx)

        band_energies = self.amset_data.energies[spin][b_idx, kpoints_idx]
        mask = band_energies < self.scattering_energy_cutoffs[0]
        mask |= band_energies > self.scattering_energy_cutoffs[1]
        fill_mask = mask[self.amset_data.ir_to_full_kpoint_mapping]

        n = np.sum(~fill_mask)
        logger.info(
            "  ├── # k-points within Fermi–Dirac cut-offs: {}".format(n))

        k_idx_in_cutoff = kpoints_idx[~mask]
        ir_idx_in_cutoff = np.arange(nkpoints)[~mask]
        iterable = list(zip(k_idx_in_cutoff, ir_idx_in_cutoff))

        to_stack = []
        if len(self.basic_scatterers) > 0:
            basic_rates = np.array([
                m.rates[spin][:, :, b_idx, kpoints_idx]
                for m in self.basic_scatterers
            ])
            to_stack.append(basic_rates)

        if len(self.elastic_scatterers) > 0:
            elastic_prefactors = conversion * np.array(
                [m.prefactor(spin, b_idx) for m in self.elastic_scatterers])
            elastic_rates = np.zeros(elastic_prefactors.shape + (nkpoints, ))

            if len(k_idx_in_cutoff) > 0:
                if self.progress_bar:
                    pbar = get_progress_bar(iterable, desc="elastic")
                else:
                    pbar = iterable
                for k_idx, ir_idx in pbar:
                    elastic_rates[..., ir_idx] = self.calculate_rate(
                        spin, b_idx, k_idx)

            elastic_rates *= elastic_prefactors[..., None]
            to_stack.append(elastic_rates)

        if len(self.inelastic_scatterers) > 0:
            inelastic_prefactors = conversion * np.array(
                [m.prefactor(spin, b_idx) for m in self.inelastic_scatterers])
            inelastic_rates = np.zeros(inelastic_prefactors.shape +
                                       (nkpoints, ))
            f_pop = self.settings["pop_frequency"]
            energy_diff = f_pop * 1e12 * 2 * np.pi * hbar * ev_to_hartree

            if len(k_idx_in_cutoff) > 0:
                if self.progress_bar:
                    pbar = get_progress_bar(iterable, desc="inelastic")
                else:
                    pbar = iterable
                inelastic_rates[:, :, :, ir_idx_in_cutoff] = 0
                for k_idx, ir_idx in pbar:
                    for ediff in [energy_diff, -energy_diff]:
                        inelastic_rates[:, :, :,
                                        ir_idx] += self.calculate_rate(
                                            spin,
                                            b_idx,
                                            k_idx,
                                            energy_diff=ediff)

            inelastic_rates *= inelastic_prefactors[..., None]
            to_stack.append(inelastic_rates)

        all_band_rates = np.vstack(to_stack)

        return all_band_rates[
            ..., self.amset_data.ir_to_full_kpoint_mapping], fill_mask
Ejemplo n.º 6
0
    def get_spin_density_of_states(
        self,
        spin,
        energies,
        integrand=None,
        band_idx=None,
        use_cached_weights=False,
        progress_bar=False,
    ):
        # integrand should have the shape (nbands, n_ir_kpts, 3, 3)
        # the integrand should have been summed at all equivalent k-points
        # TODO: add support for variable shaped integrands
        if integrand is None:
            dos = np.zeros_like(energies)
        else:
            dos = np.zeros((len(energies), 3, 3))

        if use_cached_weights:
            if self._weights_cache is None:
                raise ValueError("No integrand have been cached")

            all_weights = self._weights_cache[spin]
            all_weights_mask = self._weights_mask_cache[spin]
            energies = self._energies_cache[spin]
        else:
            all_weights = []
            all_weights_mask = []

        nbands = len(self.energies[spin])
        kpoint_multiplicity = np.tile(self.ir_kpoint_weights, (nbands, 1))

        if band_idx is not None and integrand is not None:
            integrand = integrand[band_idx]

        if band_idx is not None and integrand is None:
            kpoint_multiplicity = kpoint_multiplicity[band_idx]

        energies_iter = list(enumerate(energies))
        if progress_bar:
            energies_iter = get_progress_bar(iterable=energies_iter,
                                             desc="DOS")

        for i, energy in energies_iter:
            if use_cached_weights:
                weights = all_weights[i]
                weights_mask = all_weights_mask[i]
            else:
                weights = self.get_energy_dependent_integration_weights(
                    spin, energy)
                weights_mask = weights != 0
                all_weights.append(weights)
                all_weights_mask.append(weights_mask)

            if band_idx is not None:
                weights = weights[band_idx]
                weights_mask = weights_mask[band_idx]

            if integrand is None:
                dos[i] = np.sum(weights[weights_mask] *
                                kpoint_multiplicity[weights_mask])

            else:
                # don't need to include the k-point multiplicity as this is included by
                # pre-summing the integrand at symmetry equivalent points
                dos[i] = np.sum(weights[weights_mask, None, None] *
                                integrand[weights_mask],
                                axis=0)

        if not use_cached_weights:
            self._weights_cache[spin] = np.array(all_weights)
            self._weights_mask_cache[spin] = np.array(all_weights_mask)
            self._energies_cache[spin] = energies

        return energies, np.asarray(dos)
Ejemplo n.º 7
0
    def initialize_workers(self):
        if self._basic_only:
            return

        logger.info(
            f"Forking {self.nworkers} processes to calculate scattering")
        t0 = time.perf_counter()

        if isinstance(self.amset_data.overlap_calculator,
                      ProjectionOverlapCalculator):
            overlap_type = "projection"
        else:
            overlap_type = "wavefunction"

        if self._coeffs is None:
            coeffs_buffer = None
            coeffs_mapping_buffer = None
        else:
            coeffs_buffer, self._coeffs = create_shared_dict_array(
                self._coeffs, return_shared_data=True)
            coeffs_mapping_buffer, self._coeffs_mapping = create_shared_dict_array(
                self._coeffs_mapping, return_shared_data=True)

        amset_data_min = _AmsetDataMin.from_amset_data(self.amset_data)
        amset_data_min_reference = amset_data_min.to_reference()

        # deformation potential is a large tensor that should be put into shared memory
        elastic_scatterers = [
            s.to_reference() if isinstance(
                s, AcousticDeformationPotentialScattering) else s
            for s in self.elastic_scatterers
        ]

        ctx = multiprocessing.get_context("spawn")
        self.in_queue = ctx.Queue()
        self.out_queue = ctx.Queue()
        args = (
            self.amset_data.tetrahedral_band_structure.to_reference(),
            overlap_type,
            self.amset_data.overlap_calculator.to_reference(),
            self.amset_data.mrta_calculator.to_reference(),
            elastic_scatterers,
            self.inelastic_scatterers,
            amset_data_min_reference,
            coeffs_buffer,
            coeffs_mapping_buffer,
            self.in_queue,
            self.out_queue,
        )

        self.workers = []
        for _ in range(self.nworkers):
            self.workers.append(
                ctx.Process(target=scattering_worker, args=args))

        iterable = self.workers
        if self.progress_bar:
            iterable = get_progress_bar(self.workers, desc="workers")

        for w in iterable:
            w.start()

        log_time_taken(t0)
        return self.workers
Ejemplo n.º 8
0
def desymmetrize_coefficients(
    coeffs,
    gpoints,
    kpoints,
    structure,
    rotations,
    translations,
    is_tr,
    op_mapping,
    kp_mapping,
    pbar=True,
):
    logger.info("Desymmetrizing wavefunction coefficients")
    t0 = time.perf_counter()

    ncl = is_ncl(coeffs)
    rots = rotations[op_mapping]
    taus = translations[op_mapping]
    trs = is_tr[op_mapping]

    su2s = None
    if ncl:
        # get cartesian rotation matrix
        r_cart = [
            similarity_transformation(structure.lattice.matrix.T, r.T)
            for r in rotations
        ]

        # calculate SU(2)
        su2_no_dagger = np.array([rotation_matrix_to_su2(r) for r in r_cart])

        # calculate SU(2)^{dagger}
        su2 = np.conjugate(su2_no_dagger).transpose((0, 2, 1))
        su2s = su2[op_mapping]

    g_mesh = (np.abs(gpoints).max(axis=0) + 3) * 2
    g1, g2, g3 = (gpoints + g_mesh / 2).astype(int).T  # indices of g-points to keep

    all_rot_coeffs = {}
    for spin, spin_coeffs in coeffs.items():
        coeff_shape = (len(spin_coeffs), len(rots)) + tuple(g_mesh)
        if ncl:
            coeff_shape += (2,)
        rot_coeffs = np.zeros(coeff_shape, dtype=complex)

        state_idxs = list(range(len(rots)))
        if pbar:
            state_idxs = get_progress_bar(state_idxs, desc="progress")

        for k_idx in state_idxs:
            map_idx = kp_mapping[k_idx]

            rot = rots[k_idx]
            tau = taus[k_idx]
            tr = trs[k_idx]
            kpoint = kpoints[map_idx]

            rot_kpoint = np.dot(rot, kpoint)
            kdiff = np.around(rot_kpoint)
            rot_kpoint -= kdiff

            edges = np.around(rot_kpoint, 5) == -0.5
            rot_kpoint += edges
            kdiff -= edges

            rot_gpoints = np.dot(rot, gpoints.T).T
            rot_gpoints = np.around(rot_gpoints).astype(int)
            rot_gpoints += kdiff.astype(int)

            if tr:
                tau = -tau

            factor = np.exp(-1j * 2 * np.pi * np.dot(rot_gpoints + rot_kpoint, tau))
            rg1, rg2, rg3 = (rot_gpoints + g_mesh / 2).astype(int).T

            if ncl:
                # perform rotation in spin space
                su2 = su2s[k_idx]
                rc = np.zeros_like(spin_coeffs[:, map_idx])
                rc[:, :, 0] = (
                    su2[0, 0] * spin_coeffs[:, map_idx, :, 0]
                    + su2[0, 1] * spin_coeffs[:, map_idx, :, 1]
                )
                rc[:, :, 1] = (
                    su2[1, 0] * spin_coeffs[:, map_idx, :, 0]
                    + su2[1, 1] * spin_coeffs[:, map_idx, :, 1]
                )
                rot_coeffs[:, k_idx, rg1, rg2, rg3] = factor[None, :, None] * rc
            else:
                rot_coeffs[:, k_idx, rg1, rg2, rg3] = spin_coeffs[:, map_idx] * factor

            if tr and not ncl:
                rot_coeffs[:, k_idx] = np.conjugate(rot_coeffs[:, k_idx])

        all_rot_coeffs[spin] = rot_coeffs[:, :, g1, g2, g3]

    log_time_taken(t0)
    return all_rot_coeffs
Ejemplo n.º 9
0
def test_get_progress_bar(iterable, total, error):
    with error:
        pbar = get_progress_bar(iterable=iterable, total=total)
        pbar.close()
Ejemplo n.º 10
0
    def calculate_band_rates(self, spin: Spin, b_idx: int):
        conversion = self.amset_data.structure.lattice.reciprocal_lattice.volume

        kpoints_idx = self.amset_data.ir_kpoints_idx
        nkpoints = len(kpoints_idx)

        buf = 0.01 * 5 * units.eV
        band_energies = self.amset_data.energies[spin][b_idx, kpoints_idx]
        mask = (band_energies < self.scattering_energy_cutoffs[0] - buf) | (
            band_energies > self.scattering_energy_cutoffs[1] + buf)
        fill_mask = mask[self.amset_data.ir_to_full_kpoint_mapping]

        n = np.sum(~fill_mask)
        logger.debug(
            "  ├── # k-points within Fermi–Dirac cut-offs: {}".format(n))

        # get k-point indexes of k-points within FD cutoffs (faster than np.where)
        k_idx_in_cutoff = np.arange(nkpoints)[~mask]

        to_stack = []
        if len(self.basic_scatterers) > 0:
            basic_rates = np.array([
                m.rates[spin][:, :, b_idx, kpoints_idx]
                for m in self.basic_scatterers
            ])
            to_stack.append(basic_rates)

        if len(self.elastic_scatterers) > 0:
            elastic_prefactors = conversion * np.array(
                [m.prefactor(spin, b_idx) for m in self.elastic_scatterers])
            elastic_rates = np.zeros(elastic_prefactors.shape + (nkpoints, ))

            if len(k_idx_in_cutoff) > 0:
                pbar = get_progress_bar(k_idx_in_cutoff, desc="elastic")
                for k_idx in pbar:
                    elastic_rates[..., k_idx] = self.calculate_rate(
                        spin, b_idx, k_idx)

            elastic_rates *= elastic_prefactors[..., None]
            to_stack.append(elastic_rates)

        if len(self.inelastic_scatterers) > 0:
            inelastic_prefactors = conversion * np.array(
                [m.prefactor(spin, b_idx) for m in self.inelastic_scatterers])
            inelastic_rates = np.zeros(inelastic_prefactors.shape +
                                       (nkpoints, ))
            f_pop = self.settings["pop_frequency"]
            energy_diff = f_pop * 1e12 * 2 * np.pi * hbar * units.eV

            if len(k_idx_in_cutoff) > 0:
                pbar = get_progress_bar(k_idx_in_cutoff, desc="inelastic")
                inelastic_rates[:, :, :, k_idx_in_cutoff] = 0
                for k_idx in pbar:
                    for ediff in [energy_diff, -energy_diff]:
                        inelastic_rates[:, :, :, k_idx] += self.calculate_rate(
                            spin, b_idx, k_idx, energy_diff=ediff)

            inelastic_rates *= inelastic_prefactors[..., None]
            to_stack.append(inelastic_rates)

        all_band_rates = np.vstack(to_stack)

        return all_band_rates[
            ..., self.amset_data.ir_to_full_kpoint_mapping], fill_mask