Ejemplo n.º 1
0
    def __init__(
        self,
        band_structure: BandStructure,
        num_electrons: int,
        interpolation_factor: float = defaults["interpolation_factor"],
        soc: bool = False,
        magmom: Optional[np.ndarray] = None,
        mommat: Optional[np.ndarray] = None,
        other_properties: Dict[Spin, Dict[str, np.ndarray]] = None,
    ):
        self._band_structure = band_structure
        self._num_electrons = num_electrons
        self._soc = soc
        self._spins = self._band_structure.bands.keys()
        self._other_properties = other_properties
        self.interpolation_factor = interpolation_factor
        self._lattice_matrix = (band_structure.structure.lattice.matrix.T *
                                angstrom_to_bohr)
        self._coefficients = {}
        self._other_coefficients = defaultdict(dict)

        kpoints = np.array([k.frac_coords for k in band_structure.kpoints])
        atoms = AseAtomsAdaptor.get_atoms(band_structure.structure)

        logger.info("Getting band interpolation coefficients")

        t0 = time.perf_counter()
        self._equivalences = sphere.get_equivalences(atoms=atoms,
                                                     nkpt=kpoints.shape[0] *
                                                     interpolation_factor,
                                                     magmom=magmom)

        # get the interpolation mesh used by BoltzTraP2
        self.interpolation_mesh = (
            2 * np.max(np.abs(np.vstack(self._equivalences)), axis=0) + 1)

        for spin in self._spins:
            energies = band_structure.bands[spin] * ev_to_hartree
            data = DFTData(kpoints,
                           energies,
                           self._lattice_matrix,
                           mommat=mommat)
            self._coefficients[spin] = fite.fitde3D(data, self._equivalences)

        log_time_taken(t0)

        t0 = time.perf_counter()
        if self._other_properties:
            logger.info("Getting additional interpolation coefficients")

            for spin in self._spins:
                for label, prop in self._other_properties[spin].items():
                    data = DFTData(kpoints,
                                   prop,
                                   self._lattice_matrix,
                                   mommat=mommat)
                    self._other_coefficients[spin][label] = fite.fitde3D(
                        data, self._equivalences)
            log_time_taken(t0)
Ejemplo n.º 2
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.º 3
0
    def calculate_dos(
        self,
        estep: float = defaults["dos_estep"],
        progress_bar: bool = defaults["print_log"]
    ):
        """
        Args:
            estep: The DOS energy step in eV, where smaller numbers give more
                accuracy but are more expensive.
            progress_bar: Show a progress bar for DOS calculation.
        """
        emin = np.min([np.min(spin_eners) for spin_eners in self.energies.values()])
        emax = np.max([np.max(spin_eners) for spin_eners in self.energies.values()])
        epoints = int(round((emax - emin) / (estep * units.eV)))
        energies = np.linspace(emin, emax, epoints)
        dos_weight = 1 if self._soc or len(self.spins) == 2 else 2

        logger.debug("DOS parameters:")
        log_list(
            [
                "emin: {:.2f} eV".format(emin / units.eV),
                "emax: {:.2f} eV".format(emax / units.eV),
                "dos weight: {}".format(dos_weight),
                "n points: {}".format(epoints),
            ]
        )

        logger.debug("Generating tetrahedral DOS:")
        t0 = time.perf_counter()
        emesh, dos = self.tetrahedral_band_structure.get_density_of_states(
            energies=energies, progress_bar=progress_bar
        )
        log_time_taken(t0)

        num_electrons = self.num_electrons if self.is_metal else None

        self.dos = FermiDos(
            self.intrinsic_fermi_level,
            emesh,
            dos,
            self.structure,
            atomic_units=True,
            dos_weight=dos_weight,
            num_electrons=num_electrons,
        )
Ejemplo n.º 4
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.º 5
0
def solve_boltzman_transport_equation(
    amset_data: AmsetData,
    calculate_mobility: bool = defaults["calculate_mobility"],
    separate_mobility: bool = defaults["separate_mobility"],
    progress_bar: bool = defaults["print_log"],
):
    has_doping = amset_data.doping is not None
    has_temps = amset_data.temperatures is not None
    has_rates = amset_data.scattering_rates is not None
    if not (has_doping and has_temps and has_rates):
        raise ValueError(_e_str)

    logger.info(
        "Calculating conductivity, Seebeck, and electronic thermal conductivity"
    )
    t0 = time.perf_counter()
    sigma, seebeck, kappa = _calculate_transport_properties(
        amset_data, progress_bar=progress_bar)
    log_time_taken(t0)

    if not calculate_mobility:
        return sigma, seebeck, kappa, None

    if amset_data.is_metal:
        logger.info(
            "System is metallic, refusing to calculate carrier mobility")
        return sigma, seebeck, kappa, None

    n_scats = len(amset_data.scattering_labels)

    logger.info("Calculating overall mobility")
    t0 = time.perf_counter()
    overall = _calculate_mobility(
        amset_data,
        np.arange(n_scats),
        pbar_label="mobility" if progress_bar else None)
    mobility = {"overall": overall}
    log_time_taken(t0)

    if separate_mobility:
        logger.info("Calculating individual scattering rate mobilities")
        t0 = time.perf_counter()
        for rate_idx, name in enumerate(amset_data.scattering_labels):
            mobility[name] = _calculate_mobility(
                amset_data,
                rate_idx,
                pbar_label=name if progress_bar else None)
        log_time_taken(t0)

    return sigma, seebeck, kappa, mobility
Ejemplo n.º 6
0
    def get_energies(
        self,
        kpoints: Union[np.ndarray, List],
        energy_cutoff: Optional[float] = None,
        scissor: float = None,
        bandgap: float = None,
        return_velocity: bool = False,
        return_curvature: bool = False,
        return_other_properties: bool = False,
        coords_are_cartesian: bool = False,
        atomic_units: bool = False,
        symprec: Optional[float] = defaults["symprec"],
        return_efermi: bool = False,
        return_vb_idx: bool = False,
    ) -> Union[Dict[Spin, np.ndarray], Tuple[Dict[Spin, np.ndarray], ...]]:
        """Gets the interpolated energies for multiple k-points in a band.

        Note, the accuracy of the interpolation is dependant on the
        ``interpolate_factor`` used to initialize the Interpolater.

        Args:
            kpoints: The k-point coordinates.
            energy_cutoff: The energy cut-off to determine which bands are
                included in the interpolation. If the energy of a band falls
                within the cut-off at any k-point it will be included. For
                metals the range is defined as the Fermi level ± energy_cutoff.
                For gapped materials, the energy range is from the VBM -
                energy_cutoff to the CBM + energy_cutoff.
            scissor: The amount by which the band gap is scissored. Cannot
                be used in conjunction with the ``bandgap`` option. Has no
                effect for metallic systems.
            bandgap: Automatically adjust the band gap to this value. Cannot
                be used in conjunction with the ``scissor`` option. Has no
                effect for metallic systems.
            return_velocity: Whether to return the band velocities.
            return_curvature: Whether to return the band curvature (inverse effective
                mass).
            return_other_properties: Whether to return the interpolated results
                for the ``other_properties`` data.
            coords_are_cartesian: Whether the kpoints are in cartesian or
                fractional coordinates.
            atomic_units: Return the energies, velocities, and effective_massses
                in atomic units. If False, energies will be in eV, velocities in
                cm/s, and curvature in units of 1 / electron rest mass (1/m0).
            symprec: Symmetry precision. If set, symmetry will be used to
                reduce the nummber of calculated k-points and velocities.
            return_efermi: Whether to return the Fermi level with the unit
                determined by ``atomic_units``. If the system is semiconducting
                the Fermi level will be given in the middle of the band gap.
            return_vb_idx: Whether to return the index of the highest valence band
                in the interpolated bands. Will be returned as a dictionary of
                ``{spin: vb_idx}``.

        Returns:
            The band energies as dictionary of::

                {spin: energies}

            If ``return_velocity``, ``curvature`` or ``return_other_properties`` a tuple
            is returned, formatted as::

                (energies, Optional[velocities], Optional[curvature],
                 Optional[other_properties])

            The velocities and effective masses are given as the 1x3 trace and
            full 3x3 tensor, respectively (along cartesian directions). The
            projections are summed for each orbital type (s, p, d) across all
            atoms, and are given as::

                {spin: {orbital: projections}}
        """
        if self._band_structure.is_metal() and (bandgap or scissor):
            raise ValueError("{} option set but system is metallic".format(
                "bandgap" if bandgap else "scissor"))

        # only calculate the energies for the bands within the energy cutoff
        lattice = self._band_structure.structure.lattice
        kpoints = np.asarray(kpoints)
        nkpoints = len(kpoints)

        if coords_are_cartesian:
            kpoints = lattice.reciprocal_lattice.get_fractional_coords(kpoints)

        if symprec:
            logger.info("Reducing # k-points using symmetry")
            (
                kpoints,
                weights,
                ir_kpoints_idx,
                ir_to_full_idx,
                _,
                rot_mapping,
            ) = get_symmetry_equivalent_kpoints(
                self._band_structure.structure,
                kpoints,
                symprec=symprec,
                return_inverse=True,
                time_reversal_symmetry=not self._soc,
            )
            k_info = [
                "# original k-points: {}".format(nkpoints),
                "# reduced k-points {}".format(len(kpoints)),
            ]
            log_list(k_info)

        ibands = get_ibands(energy_cutoff, self._band_structure)
        new_vb_idx = get_vb_idx(energy_cutoff, self._band_structure)

        energies = {}
        velocities = {}
        curvature = {}
        other_properties = defaultdict(dict)
        for spin in self._spins:
            spin_ibands = ibands[spin]
            min_b = spin_ibands.min() + 1
            max_b = spin_ibands.max() + 1
            info = "Interpolating {} bands {}-{}".format(
                spin_name[spin], min_b, max_b)
            logger.info(info)

            t0 = time.perf_counter()
            fitted = fite.getBands(
                kpoints,
                self._equivalences,
                self._lattice_matrix,
                self._coefficients[spin][spin_ibands],
                curvature=return_curvature,
            )
            log_time_taken(t0)

            energies[spin] = fitted[0]
            velocities[spin] = fitted[1]

            if return_curvature:
                # make curvature have the shape ((nbands, nkpoints, 3, 3)
                curvature[spin] = fitted[2]
                curvature[spin] = curvature[spin].transpose((2, 3, 0, 1))

            if return_other_properties:
                logger.info("Interpolating {} properties".format(
                    spin_name[spin]))

                t0 = time.perf_counter()
                for label, coeffs in self._other_coefficients[spin].items():
                    other_properties[spin][label], _ = fite.getBands(
                        kpoints,
                        self._equivalences,
                        self._lattice_matrix,
                        coeffs[spin_ibands],
                        curvature=False,
                    )

                log_time_taken(t0)

            if not atomic_units:
                energies[spin] = energies[spin] * hartree_to_ev
                velocities[spin] = _convert_velocities(velocities[spin],
                                                       lattice.matrix)

        if symprec:
            energies, velocities, curvature, other_properties = symmetrize_results(
                energies,
                velocities,
                curvature,
                other_properties,
                ir_to_full_idx,
                rot_mapping,
                self._band_structure.structure.lattice.reciprocal_lattice.
                matrix,
            )

        if not self._band_structure.is_metal():
            energies = _shift_energies(energies,
                                       new_vb_idx,
                                       scissor=scissor,
                                       bandgap=bandgap)

        to_return = [energies]

        if return_velocity:
            to_return.append(velocities)

        if return_curvature:
            to_return.append(curvature)

        if return_other_properties:
            to_return.append(other_properties)

        if return_efermi:
            if self._band_structure.is_metal():
                efermi = self._band_structure.efermi
                if atomic_units:
                    efermi *= ev_to_hartree
            else:
                # if semiconducting, set Fermi level to middle of gap
                efermi = _get_efermi(energies, new_vb_idx)

            to_return.append(efermi)

        if return_vb_idx:
            to_return.append(new_vb_idx)

        if len(to_return) == 1:
            return to_return[0]
        else:
            return tuple(to_return)
Ejemplo n.º 7
0
    def get_amset_data(
        self,
        energy_cutoff: Optional[float] = None,
        scissor: float = None,
        bandgap: float = None,
        symprec: float = defaults["symprec"],
        nworkers: int = defaults["nworkers"],
    ) -> AmsetData:
        """Gets an AmsetData object using the interpolated bands.

        Note, the interpolation mesh is determined using by
        ``interpolate_factor`` option in the ``Inteprolater`` constructor.

        This method is much faster than the ``get_energies`` function but
        doesn't provide as much flexibility.

        The degree of parallelization is controlled by the ``nworkers`` option.

        Args:
            energy_cutoff: The energy cut-off to determine which bands are
                included in the interpolation. If the energy of a band falls
                within the cut-off at any k-point it will be included. For
                metals the range is defined as the Fermi level ± energy_cutoff.
                For gapped materials, the energy range is from the VBM -
                energy_cutoff to the CBM + energy_cutoff.
            scissor: The amount by which the band gap is scissored. Cannot
                be used in conjunction with the ``bandgap`` option. Has no
                effect for metallic systems.
            bandgap: Automatically adjust the band gap to this value. Cannot
                be used in conjunction with the ``scissor`` option. Has no
                effect for metallic systems.
            symprec: The symmetry tolerance used when determining the symmetry
                inequivalent k-points on which to interpolate.
            nworkers: The number of processors used to perform the
                interpolation. If set to ``-1``, the number of workers will
                be set to the number of CPU cores.

        Returns:
            The electronic structure (including energies, velocities, density of
            states and k-point information) as an AmsetData object.
        """
        is_metal = self._band_structure.is_metal()

        if is_metal and (bandgap or scissor):
            raise ValueError("{} option set but system is metallic".format(
                "bandgap" if bandgap else "scissor"))

        nworkers = multiprocessing.cpu_count() if nworkers == -1 else nworkers

        logger.info("Interpolation parameters:")
        iinfo = [
            "k-point mesh: {}".format("x".join(
                map(str, self.interpolation_mesh))),
            "energy cutoff: {} eV".format(energy_cutoff),
        ]
        log_list(iinfo)

        ibands = get_ibands(energy_cutoff, self._band_structure)
        new_vb_idx = get_vb_idx(energy_cutoff, self._band_structure)

        energies = {}
        vvelocities = {}
        velocities = {}
        forgotten_electrons = 0
        for spin in self._spins:
            spin_ibands = ibands[spin]
            min_b = spin_ibands.min() + 1
            max_b = spin_ibands.max() + 1
            info = "Interpolating {} bands {}-{}".format(
                spin_name[spin], min_b, max_b)
            logger.info(info)

            # these are bands beneath the Fermi level that are dropped
            forgotten_electrons += min_b - 1

            t0 = time.perf_counter()
            energies[spin], vvelocities[spin], _, velocities[
                spin] = get_bands_fft(
                    self._equivalences,
                    self._coefficients[spin][spin_ibands],
                    self._lattice_matrix,
                    return_effective_mass=False,
                    nworkers=nworkers,
                )
            log_time_taken(t0)

        if not self._soc and len(self._spins) == 1:
            forgotten_electrons *= 2
        nelectrons = self._num_electrons - forgotten_electrons

        if is_metal:
            efermi = self._band_structure.efermi * ev_to_hartree

        else:
            energies = _shift_energies(energies,
                                       new_vb_idx,
                                       scissor=scissor,
                                       bandgap=bandgap)

            # if material is semiconducting, set Fermi level to middle of gap
            efermi = _get_efermi(energies, new_vb_idx)

        # get the actual k-points used in the BoltzTraP2 interpolation
        # unfortunately, BoltzTraP2 doesn't expose this information so we
        # have to get it ourselves
        (
            ir_kpts,
            _,
            full_kpts,
            ir_kpts_idx,
            ir_to_full_idx,
            tetrahedra,
            *ir_tetrahedra_info,
        ) = get_kpoints_tetrahedral(
            self.interpolation_mesh,
            self._band_structure.structure,
            symprec=symprec,
            time_reversal_symmetry=not self._soc,
        )

        energies, vvelocities, velocities = sort_amset_results(
            full_kpts, energies, vvelocities, velocities)
        atomic_structure = get_atomic_structure(self._band_structure.structure)

        return AmsetData(
            atomic_structure,
            energies,
            vvelocities,
            velocities,
            self.interpolation_mesh,
            full_kpts,
            ir_kpts,
            ir_kpts_idx,
            ir_to_full_idx,
            tetrahedra,
            ir_tetrahedra_info,
            efermi,
            nelectrons,
            is_metal,
            self._soc,
            vb_idx=new_vb_idx,
        )
Ejemplo n.º 8
0
    def from_data(
        cls,
        energies: Dict[Spin, np.ndarray],
        kpoints: np.ndarray,
        tetrahedra: np.ndarray,
        structure: Structure,
        ir_kpoints_idx: np.ndarray,
        ir_kpoint_mapping: np.ndarray,
        ir_tetrahedra_idx: Optional[np.ndarray] = None,
        ir_tetrahedra_to_full_idx: Optional[np.ndarray] = None,
        ir_tetrahedra_weights: Optional[np.ndarray] = None,
    ):
        logger.info("Initializing tetrahedron band structure")
        t0 = time.perf_counter()

        tparams = (ir_tetrahedra_idx, ir_tetrahedra_to_full_idx,
                   ir_tetrahedra_weights)
        if len(set([x is None for x in tparams])) != 1:
            raise ValueError(
                "Either all or none of ir_tetrahedra_idx, ir_tetrahedra_to_full_idx and"
                " ir_tetrahedra_weights should be set.")

        if ir_tetrahedra_idx is None:
            ir_tetrahedra_idx = np.arange(len(kpoints))
            ir_tetrahedra_to_full_idx = np.ones_like(ir_tetrahedra_idx)
            ir_tetrahedra_weights = np.ones_like(ir_tetrahedra_idx)

        ir_tetrahedra_to_full_idx = ir_tetrahedra_to_full_idx
        ir_kpoints_idx = ir_kpoints_idx
        ir_kpoint_mapping = ir_kpoint_mapping

        _, ir_kpoint_weights = np.unique(ir_kpoint_mapping, return_counts=True)

        # need to keep track of full tetrahedra to recover full k-point indices
        # when calculating scattering rates (i.e., k-k' is symmetry inequivalent).
        full_tetrahedra, _ = process_tetrahedra(tetrahedra, energies)

        # store irreducible tetrahedra and use energies to calculate diffs and min/maxes
        ir_tetrahedra, ir_tetrahedra_energies = process_tetrahedra(
            tetrahedra[ir_tetrahedra_idx], energies)

        # the remaining properties are given for each irreducible tetrahedra
        (e21, e31, e41, e32, e42,
         e43) = get_tetrahedra_energy_diffs(ir_tetrahedra_energies)

        (
            max_tetrahedra_energies,
            min_tetrahedra_energies,
        ) = get_max_min_tetrahedra_energies(ir_tetrahedra_energies)

        cross_section_weights = get_tetrahedra_cross_section_weights(
            structure.lattice.reciprocal_lattice.matrix,
            kpoints,
            ir_tetrahedra,
            e21,
            e31,
            e41,
        )

        tetrahedron_volume = 1 / len(tetrahedra)

        log_time_taken(t0)
        return cls(
            energies,
            kpoints,
            ir_kpoints_idx,
            ir_kpoint_mapping,
            ir_kpoint_weights,
            full_tetrahedra,
            ir_tetrahedra,
            ir_tetrahedra_energies,
            ir_tetrahedra_idx,
            ir_tetrahedra_to_full_idx,
            ir_tetrahedra_weights,
            e21,
            e31,
            e41,
            e32,
            e42,
            e43,
            max_tetrahedra_energies,
            min_tetrahedra_energies,
            cross_section_weights,
            tetrahedron_volume,
        )
Ejemplo n.º 9
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.º 10
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.º 11
0
    def __init__(
        self,
        energies: Dict[Spin, np.ndarray],
        kpoints: np.ndarray,
        tetrahedra: np.ndarray,
        structure: Structure,
        ir_kpoints_idx: np.ndarray,
        ir_kpoint_mapping: np.ndarray,
        ir_tetrahedra_idx: Optional[np.ndarray] = None,
        ir_tetrahedra_to_full_idx: Optional[np.ndarray] = None,
        ir_tetrahedra_weights: Optional[np.ndarray] = None,
    ):
        logger.info("Initializing tetrahedron band structure")
        t0 = time.perf_counter()

        tparams = (ir_tetrahedra_idx, ir_tetrahedra_to_full_idx, ir_tetrahedra_weights)
        if len(set([x is None for x in tparams])) != 1:
            raise ValueError(
                "Either all or none of ir_tetrahedra_idx, ir_tetrahedra_to_full_idx and"
                " ir_tetrahedra_weights should be set."
            )

        if ir_tetrahedra_idx is None:
            ir_tetrahedra_idx = np.arange(len(kpoints))
            ir_tetrahedra_to_full_idx = np.ones_like(ir_tetrahedra_idx)
            ir_tetrahedra_weights = np.ones_like(ir_tetrahedra_idx)

        self.energies = energies
        self.kpoints = kpoints
        self.ir_tetrahedra_idx = ir_tetrahedra_idx
        self.ir_tetrahedra_to_full_idx = ir_tetrahedra_to_full_idx
        self.ir_tetrahedra_weights = ir_tetrahedra_weights
        self.ir_kpoints_idx = ir_kpoints_idx
        self.ir_kpoint_mapping = ir_kpoint_mapping

        _, self.ir_kpoint_weights = np.unique(ir_kpoint_mapping, return_counts=True)

        # need to keep track of full tetrahedra to recover full k-point indices
        # when calculating scattering rates (i.e., k-k' is symmetry inequivalent).
        self.tetrahedra, _ = process_tetrahedra(tetrahedra, self.energies)

        # store irreducible tetrahedra and use energies to calculate diffs and min/maxes
        self.ir_tetrahedra, self.ir_tetrahedra_energies = process_tetrahedra(
            tetrahedra[self.ir_tetrahedra_idx], self.energies
        )

        # the remaining properties are given for each irreducible tetrahedra
        (
            self.e21,
            self.e31,
            self.e41,
            self.e32,
            self.e42,
            self.e43,
        ) = get_tetrahedra_energy_diffs(self.ir_tetrahedra_energies)

        (
            self.max_tetrahedra_energies,
            self.min_tetrahedra_energies,
        ) = get_max_min_tetrahedra_energies(self.ir_tetrahedra_energies)

        self.cross_section_weights = get_tetrahedra_cross_section_weights(
            structure.lattice.reciprocal_lattice.matrix,
            self.kpoints,
            self.ir_tetrahedra,
            self.e21,
            self.e31,
            self.e41,
        )

        self._tetrahedron_volume = 1 / len(tetrahedra)

        self.grouped_ir_to_full = groupby(
            np.arange(len(ir_tetrahedra_to_full_idx)), ir_tetrahedra_to_full_idx
        )

        self._ir_weights_shape = {
            s: (len(self.energies[s]), len(ir_kpoints_idx)) for s in self.energies
        }
        self._weights_cache = {}
        self._weights_mask_cache = {}
        self._energies_cache = {}

        self._tetrahedra_connections = defaultdict(set)
        for tet in tetrahedra:
            self._tetrahedra_connections[tet[0]].update(tet)
            self._tetrahedra_connections[tet[1]].update(tet)
            self._tetrahedra_connections[tet[2]].update(tet)
            self._tetrahedra_connections[tet[3]].update(tet)

        log_time_taken(t0)