Exemple #1
0
def _log_results_summary(amset_data, output_parameters):
    results_summary = []

    doping = [d * (1 / bohr_to_cm)**3 for d in amset_data.doping]
    temps = amset_data.temperatures

    if output_parameters["calculate_mobility"] and not amset_data.is_metal:
        logger.info("Average conductivity (σ), Seebeck (S) and mobility (μ)"
                    " results:")
        headers = ("conc [cm⁻³]", "temp [K]", "σ [S/m]", "S [µV/K]",
                   "μ [cm²/Vs]")
        for c, t in np.ndindex(amset_data.fermi_levels.shape):
            results = (
                doping[c],
                temps[t],
                tensor_average(amset_data.conductivity[c, t]),
                tensor_average(amset_data.seebeck[c, t]),
                tensor_average(amset_data.mobility["overall"][c, t]),
            )
            results_summary.append(results)

    else:
        logger.info("Average conductivity (σ) and Seebeck (S) results:")
        headers = ("conc [cm⁻³]", "temp [K]", "σ [S/m]", "S [µV/K]")
        for c, t in np.ndindex(amset_data.fermi_levels.shape):
            results = (
                doping[c],
                temps[t],
                tensor_average(amset_data.conductivity[c, t]),
                tensor_average(amset_data.seebeck[c, t]),
            )
            results_summary.append(results)

    table = tabulate(
        results_summary,
        headers=headers,
        numalign="right",
        stralign="center",
        floatfmt=(".2e", ".1f", ".2e", ".2e", ".1f"),
    )
    logger.info(table)

    if output_parameters["separate_mobility"] and not amset_data.is_metal:
        labels = amset_data.scattering_labels
        logger.info("Mobility breakdown by scattering mechanism, in cm²/Vs:")
        headers = ["conc [cm⁻³]", "temp [K]"] + labels

        results_summary = []
        for c, t in np.ndindex(amset_data.fermi_levels.shape):
            results = [doping[c], temps[t]]
            results += [
                tensor_average(amset_data.mobility[s][c, t]) for s in labels
            ]
            results_summary.append(results)

        table = tabulate(
            results_summary,
            headers=headers,
            numalign="right",
            stralign="center",
            floatfmt=[".2e", ".1f"] + [".2e"] * len(labels),
        )
        logger.info(table)
Exemple #2
0
    def calculate_fd_cutoffs(
        self,
        fd_tolerance: Optional[float] = 0.01,
        cutoff_pad: float = 0.0,
        max_moment: int = 2,
        mobility_rates_only: bool = False,
    ):
        energies = self.dos.energies
        vv = {
            s: v.transpose((0, 3, 1, 2))
            for s, v in self.velocities_product.items()
        }
        _, vvdos = self.tetrahedral_band_structure.get_density_of_states(
            energies, integrand=vv, sum_spins=True, use_cached_weights=True)
        vvdos = tensor_average(vvdos)
        # vvdos = np.array(self.dos.get_densities())

        # three fermi integrals govern transport properties:
        #   1. df/de controls conductivity and mobility
        #   2. (e-u) * df/de controls Seebeck
        #   3. (e-u)^2 df/de controls electronic thermal conductivity
        # take the absolute sum of the integrals across all doping and
        # temperatures. this gives us the energies that are important for
        # transport
        if fd_tolerance:

            def get_min_max_cutoff(cumsum):
                min_idx = np.where(cumsum < fd_tolerance / 2)[0].max()
                max_idx = np.where(cumsum > (1 - fd_tolerance / 2))[0].min()
                return energies[min_idx], energies[max_idx]

            min_cutoff = np.inf
            max_cutoff = -np.inf
            for n, t in np.ndindex(self.fermi_levels.shape):
                ef = self.fermi_levels[n, t]
                temp = self.temperatures[t]
                dfde = -dfdde(energies, ef, temp * boltzmann_au)

                for moment in range(max_moment + 1):
                    weight = np.abs((energies - ef)**moment * dfde)
                    weight_dos = weight * vvdos
                    weight_cumsum = np.cumsum(weight_dos)
                    weight_cumsum /= np.max(weight_cumsum)

                    cmin, cmax = get_min_max_cutoff(weight_cumsum)
                    min_cutoff = min(cmin, min_cutoff)
                    max_cutoff = max(cmax, max_cutoff)

                    # import matplotlib.pyplot as plt
                    # ax = plt.gca()
                    # plt.plot(energies / units.eV, weight / weight.max())
                    # plt.plot(energies / units.eV, vvdos / vvdos.max())
                    # plt.plot(energies / units.eV, weight_dos / weight_dos.max())
                    # plt.plot(energies / units.eV, weight_cumsum / weight_cumsum.max())
                    # ax.set(xlim=(4, 7.5))
                    # plt.show()

        else:
            min_cutoff = energies.min()
            max_cutoff = energies.max()

        if mobility_rates_only:
            vbm = get_vbm_energy(self.energies, self.vb_idx)
            cbm = get_cbm_energy(self.energies, self.vb_idx)
            mid_gap = (cbm + vbm) / 2
            if np.all(self.doping < 0):
                # only electron mobility so don't calculate valence band rates
                min_cutoff = max(min_cutoff, mid_gap)
            elif np.all(self.doping < 0):
                # only hole mobility so don't calculate conudction band rates
                max_cutoff = min(max_cutoff, mid_gap)

        min_cutoff -= cutoff_pad
        max_cutoff += cutoff_pad

        logger.info("Calculated Fermi–Dirac cut-offs:")
        log_list([
            "min: {:.3f} eV".format(min_cutoff * hartree_to_ev),
            "max: {:.3f} eV".format(max_cutoff * hartree_to_ev),
        ])
        self.fd_cutoffs = (min_cutoff, max_cutoff)
Exemple #3
0
def test_tensor_average(tensor, expected):
    assert tensor_average(tensor) == expected